LCOV - code coverage report
Current view: top level - source4/kdc - kpasswd-service.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 111 174 63.8 %
Date: 2024-04-21 15:09:00 Functions: 1 1 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Samba kpasswd implementation
       5             : 
       6             :    Copyright (c) 2005      Andrew Bartlett <abartlet@samba.org>
       7             :    Copyright (c) 2016      Andreas Schneider <asn@samba.org>
       8             : 
       9             :    This program is free software; you can redistribute it and/or modify
      10             :    it under the terms of the GNU General Public License as published by
      11             :    the Free Software Foundation; either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    This program is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU General Public License
      20             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      21             : */
      22             : 
      23             : #include "includes.h"
      24             : #include "samba/service_task.h"
      25             : #include "tsocket/tsocket.h"
      26             : #include "auth/credentials/credentials.h"
      27             : #include "auth/auth.h"
      28             : #include "auth/gensec/gensec.h"
      29             : #include "kdc/kdc-server.h"
      30             : #include "kdc/kpasswd-service.h"
      31             : #include "kdc/kpasswd-helper.h"
      32             : #include "param/param.h"
      33             : 
      34             : #undef DBGC_CLASS
      35             : #define DBGC_CLASS DBGC_KERBEROS
      36             : 
      37             : #define HEADER_LEN 6
      38             : #ifndef RFC3244_VERSION
      39             : #define RFC3244_VERSION 0xff80
      40             : #endif
      41             : 
      42         118 : kdc_code kpasswd_process(struct kdc_server *kdc,
      43             :                          TALLOC_CTX *mem_ctx,
      44             :                          DATA_BLOB *request,
      45             :                          DATA_BLOB *reply,
      46             :                          struct tsocket_address *remote_addr,
      47             :                          struct tsocket_address *local_addr,
      48             :                          int datagram)
      49             : {
      50           0 :         uint16_t len;
      51           0 :         uint16_t verno;
      52           0 :         uint16_t ap_req_len;
      53           0 :         uint16_t enc_data_len;
      54         118 :         DATA_BLOB ap_req_blob = data_blob_null;
      55         118 :         DATA_BLOB ap_rep_blob = data_blob_null;
      56         118 :         DATA_BLOB enc_data_blob = data_blob_null;
      57         118 :         DATA_BLOB dec_data_blob = data_blob_null;
      58         118 :         DATA_BLOB kpasswd_dec_reply = data_blob_null;
      59         118 :         const char *error_string = NULL;
      60         118 :         krb5_error_code error_code = 0;
      61           0 :         struct cli_credentials *server_credentials;
      62           0 :         struct gensec_security *gensec_security;
      63             : #ifndef SAMBA4_USES_HEIMDAL
      64             :         struct sockaddr_storage remote_ss;
      65             : #endif
      66           0 :         struct sockaddr_storage local_ss;
      67           0 :         ssize_t socklen;
      68           0 :         TALLOC_CTX *tmp_ctx;
      69         118 :         kdc_code rc = KDC_ERROR;
      70         118 :         krb5_error_code code = 0;
      71           0 :         NTSTATUS status;
      72           0 :         int rv;
      73           0 :         bool is_inet;
      74           0 :         bool ok;
      75             : 
      76         118 :         if (kdc->am_rodc) {
      77           0 :                 return KDC_PROXY_REQUEST;
      78             :         }
      79             : 
      80         118 :         tmp_ctx = talloc_new(mem_ctx);
      81         118 :         if (tmp_ctx == NULL) {
      82           0 :                 return KDC_ERROR;
      83             :         }
      84             : 
      85         118 :         is_inet = tsocket_address_is_inet(remote_addr, "ip");
      86         118 :         if (!is_inet) {
      87           0 :                 DBG_WARNING("Invalid remote IP address\n");
      88           0 :                 goto done;
      89             :         }
      90             : 
      91             : #ifndef SAMBA4_USES_HEIMDAL
      92             :         /*
      93             :          * FIXME: Heimdal fails to to do a krb5_rd_req() in gensec_krb5 if we
      94             :          * set the remote address.
      95             :          */
      96             : 
      97             :         /* remote_addr */
      98          50 :         socklen = tsocket_address_bsd_sockaddr(remote_addr,
      99             :                                                (struct sockaddr *)&remote_ss,
     100             :                                                sizeof(struct sockaddr_storage));
     101          50 :         if (socklen < 0) {
     102           0 :                 DBG_WARNING("Invalid remote IP address\n");
     103           0 :                 goto done;
     104             :         }
     105             : #endif
     106             : 
     107             :         /* local_addr */
     108         118 :         socklen = tsocket_address_bsd_sockaddr(local_addr,
     109             :                                                (struct sockaddr *)&local_ss,
     110             :                                                sizeof(struct sockaddr_storage));
     111         118 :         if (socklen < 0) {
     112           0 :                 DBG_WARNING("Invalid local IP address\n");
     113           0 :                 goto done;
     114             :         }
     115             : 
     116         118 :         if (request->length <= HEADER_LEN) {
     117           0 :                 DBG_WARNING("Request truncated\n");
     118           0 :                 goto done;
     119             :         }
     120             : 
     121         118 :         len = RSVAL(request->data, 0);
     122         118 :         if (request->length != len) {
     123           0 :                 DBG_WARNING("Request length does not match\n");
     124           0 :                 goto done;
     125             :         }
     126             : 
     127         118 :         verno = RSVAL(request->data, 2);
     128         118 :         if (verno != 1 && verno != RFC3244_VERSION) {
     129           0 :                 DBG_WARNING("Unsupported version: 0x%04x\n", verno);
     130             :         }
     131             : 
     132         118 :         ap_req_len = RSVAL(request->data, 4);
     133         118 :         if ((ap_req_len >= len) || ((ap_req_len + HEADER_LEN) >= len)) {
     134           0 :                 DBG_WARNING("AP_REQ truncated\n");
     135           0 :                 goto done;
     136             :         }
     137             : 
     138         118 :         ap_req_blob = data_blob_const(&request->data[HEADER_LEN], ap_req_len);
     139             : 
     140         118 :         enc_data_len = len - ap_req_len;
     141         118 :         enc_data_blob = data_blob_const(&request->data[HEADER_LEN + ap_req_len],
     142             :                                         enc_data_len);
     143             : 
     144         118 :         server_credentials = cli_credentials_init(tmp_ctx);
     145         118 :         if (server_credentials == NULL) {
     146           0 :                 DBG_ERR("Failed to initialize server credentials!\n");
     147           0 :                 goto done;
     148             :         }
     149             : 
     150             :         /*
     151             :          * We want the credentials subsystem to use the krb5 context we already
     152             :          * have, rather than a new context.
     153             :          *
     154             :          * On this context the KDB plugin has been loaded, so we can access
     155             :          * dsdb.
     156             :          */
     157         118 :         status = cli_credentials_set_krb5_context(server_credentials,
     158             :                                                   kdc->smb_krb5_context);
     159         118 :         if (!NT_STATUS_IS_OK(status)) {
     160           0 :                 goto done;
     161             :         }
     162             : 
     163         118 :         ok = cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
     164         118 :         if (!ok) {
     165           0 :                 goto done;
     166             :         }
     167             : 
     168             :         /*
     169             :          * After calling cli_credentials_set_conf(), explicitly set the realm
     170             :          * with CRED_SPECIFIED. We need to do this so the result of
     171             :          * principal_from_credentials() called from the gensec layer is
     172             :          * CRED_SPECIFIED rather than CRED_SMB_CONF, avoiding a fallback to
     173             :          * match-by-key (very undesirable in this case).
     174             :          */
     175         118 :         ok = cli_credentials_set_realm(server_credentials,
     176         118 :                                        lpcfg_realm(kdc->task->lp_ctx),
     177             :                                        CRED_SPECIFIED);
     178         118 :         if (!ok) {
     179           0 :                 goto done;
     180             :         }
     181             : 
     182         118 :         ok = cli_credentials_set_username(server_credentials,
     183             :                                           "kadmin/changepw",
     184             :                                           CRED_SPECIFIED);
     185         118 :         if (!ok) {
     186           0 :                 goto done;
     187             :         }
     188             : 
     189             :         /* Check that the server principal is indeed CRED_SPECIFIED. */
     190             :         {
     191         118 :                 char *principal = NULL;
     192           0 :                 enum credentials_obtained obtained;
     193             : 
     194         118 :                 principal = cli_credentials_get_principal_and_obtained(server_credentials,
     195             :                                                                        tmp_ctx,
     196             :                                                                        &obtained);
     197         118 :                 if (obtained < CRED_SPECIFIED) {
     198           0 :                         goto done;
     199             :                 }
     200             : 
     201         118 :                 TALLOC_FREE(principal);
     202             :         }
     203             : 
     204         118 :         rv = cli_credentials_set_keytab_name(server_credentials,
     205         118 :                                              kdc->task->lp_ctx,
     206             :                                              kdc->kpasswd_keytab_name,
     207             :                                              CRED_SPECIFIED);
     208         118 :         if (rv != 0) {
     209           0 :                 DBG_ERR("Failed to set credentials keytab name\n");
     210           0 :                 goto done;
     211             :         }
     212             : 
     213         118 :         status = samba_server_gensec_start(tmp_ctx,
     214         118 :                                            kdc->task->event_ctx,
     215         118 :                                            kdc->task->msg_ctx,
     216         118 :                                            kdc->task->lp_ctx,
     217             :                                            server_credentials,
     218             :                                            "kpasswd",
     219             :                                            &gensec_security);
     220         118 :         if (!NT_STATUS_IS_OK(status)) {
     221           0 :                 goto done;
     222             :         }
     223             : 
     224         118 :         status = gensec_set_local_address(gensec_security, local_addr);
     225         118 :         if (!NT_STATUS_IS_OK(status)) {
     226           0 :                 goto done;
     227             :         }
     228             : 
     229             : #ifndef SAMBA4_USES_HEIMDAL
     230          50 :         status = gensec_set_remote_address(gensec_security, remote_addr);
     231          50 :         if (!NT_STATUS_IS_OK(status)) {
     232           0 :                 goto done;
     233             :         }
     234             : #endif
     235             : 
     236             :         /* We want the GENSEC wrap calls to generate PRIV tokens */
     237         118 :         gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
     238             : 
     239             :         /* Use the krb5 gesec mechanism so we can load DB modules */
     240         118 :         status = gensec_start_mech_by_name(gensec_security, "krb5");
     241         118 :         if (!NT_STATUS_IS_OK(status)) {
     242           0 :                 goto done;
     243             :         }
     244             : 
     245             :         /*
     246             :          * Accept the AP-REQ and generate the AP-REP we need for the reply
     247             :          *
     248             :          * We only allow KRB5 and make sure the backend to is RPC/IPC free.
     249             :          *
     250             :          * See gensec_krb5_update_internal() as GENSEC_SERVER.
     251             :          *
     252             :          * It allows gensec_update() not to block.
     253             :          *
     254             :          * If that changes in future we need to use
     255             :          * gensec_update_send/recv here!
     256             :          */
     257         118 :         status = gensec_update(gensec_security, tmp_ctx,
     258             :                                ap_req_blob, &ap_rep_blob);
     259         118 :         if (!NT_STATUS_IS_OK(status) &&
     260          16 :             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
     261          16 :                 ap_rep_blob = data_blob_null;
     262          16 :                 error_code = KRB5_KPASSWD_HARDERROR;
     263          16 :                 error_string = talloc_asprintf(tmp_ctx,
     264             :                                                "gensec_update failed - %s\n",
     265             :                                                nt_errstr(status));
     266          16 :                 DBG_ERR("%s", error_string);
     267          16 :                 goto reply;
     268             :         }
     269             : 
     270         102 :         status = gensec_unwrap(gensec_security,
     271             :                                tmp_ctx,
     272             :                                &enc_data_blob,
     273             :                                &dec_data_blob);
     274         102 :         if (!NT_STATUS_IS_OK(status)) {
     275           4 :                 ap_rep_blob = data_blob_null;
     276           4 :                 error_code = KRB5_KPASSWD_HARDERROR;
     277           4 :                 error_string = talloc_asprintf(tmp_ctx,
     278             :                                                "gensec_unwrap failed - %s\n",
     279             :                                                nt_errstr(status));
     280           4 :                 DBG_ERR("%s", error_string);
     281           4 :                 goto reply;
     282             :         }
     283             : 
     284          98 :         code = kpasswd_handle_request(kdc,
     285             :                                       tmp_ctx,
     286             :                                       gensec_security,
     287             :                                       verno,
     288             :                                       &dec_data_blob,
     289             :                                       &kpasswd_dec_reply,
     290             :                                       &error_string);
     291          98 :         if (code != 0) {
     292          14 :                 ap_rep_blob = data_blob_null;
     293          14 :                 error_code = code;
     294          14 :                 goto reply;
     295             :         }
     296             : 
     297          84 :         status = gensec_wrap(gensec_security,
     298             :                              tmp_ctx,
     299             :                              &kpasswd_dec_reply,
     300             :                              &enc_data_blob);
     301          84 :         if (!NT_STATUS_IS_OK(status)) {
     302           0 :                 ap_rep_blob = data_blob_null;
     303           0 :                 error_code = KRB5_KPASSWD_HARDERROR;
     304           0 :                 error_string = talloc_asprintf(tmp_ctx,
     305             :                                                "gensec_wrap failed - %s\n",
     306             :                                                nt_errstr(status));
     307           0 :                 DBG_ERR("%s", error_string);
     308           0 :                 goto reply;
     309             :         }
     310             : 
     311          84 : reply:
     312         118 :         if (error_code != 0) {
     313           0 :                 krb5_data k_enc_data;
     314           0 :                 krb5_data k_dec_data;
     315           0 :                 const char *principal_string;
     316           0 :                 krb5_principal server_principal;
     317             : 
     318          34 :                 if (error_string == NULL) {
     319           0 :                         DBG_ERR("Invalid error string! This should not happen\n");
     320           0 :                         goto done;
     321             :                 }
     322             : 
     323          34 :                 ok = kpasswd_make_error_reply(tmp_ctx,
     324             :                                               error_code,
     325             :                                               error_string,
     326             :                                               &dec_data_blob);
     327          34 :                 if (!ok) {
     328           0 :                         DBG_ERR("Failed to create error reply\n");
     329           0 :                         goto done;
     330             :                 }
     331             : 
     332          34 :                 k_dec_data = smb_krb5_data_from_blob(dec_data_blob);
     333             : 
     334          34 :                 principal_string = cli_credentials_get_principal(server_credentials,
     335             :                                                                  tmp_ctx);
     336          34 :                 if (principal_string == NULL) {
     337           0 :                         goto done;
     338             :                 }
     339             : 
     340          34 :                 code = smb_krb5_parse_name(kdc->smb_krb5_context->krb5_context,
     341             :                                            principal_string,
     342             :                                            &server_principal);
     343          34 :                 if (code != 0) {
     344           0 :                         DBG_ERR("Failed to create principal: %s\n",
     345             :                                 error_message(code));
     346           0 :                         goto done;
     347             :                 }
     348             : 
     349          34 :                 code = smb_krb5_mk_error(kdc->smb_krb5_context->krb5_context,
     350          17 :                                          KRB5KDC_ERR_NONE + error_code,
     351             :                                          NULL, /* e_text */
     352             :                                          &k_dec_data,
     353             :                                          NULL, /* client */
     354             :                                          server_principal,
     355             :                                          &k_enc_data);
     356          34 :                 krb5_free_principal(kdc->smb_krb5_context->krb5_context,
     357             :                                     server_principal);
     358          34 :                 if (code != 0) {
     359           0 :                         DBG_ERR("Failed to create krb5 error reply: %s\n",
     360             :                                 error_message(code));
     361           0 :                         goto done;
     362             :                 }
     363             : 
     364          34 :                 enc_data_blob = data_blob_talloc(tmp_ctx,
     365             :                                                  k_enc_data.data,
     366             :                                                  k_enc_data.length);
     367          34 :                 if (enc_data_blob.data == NULL) {
     368           0 :                         DBG_ERR("Failed to allocate memory for error reply\n");
     369           0 :                         goto done;
     370             :                 }
     371             :         }
     372             : 
     373         118 :         *reply = data_blob_talloc(mem_ctx,
     374             :                                   NULL,
     375             :                                   HEADER_LEN + ap_rep_blob.length + enc_data_blob.length);
     376         118 :         if (reply->data == NULL) {
     377           0 :                 goto done;
     378             :         }
     379         118 :         RSSVAL(reply->data, 0, reply->length);
     380         118 :         RSSVAL(reply->data, 2, 1);
     381         118 :         RSSVAL(reply->data, 4, ap_rep_blob.length);
     382         118 :         if (ap_rep_blob.data != NULL) {
     383          84 :                 memcpy(reply->data + HEADER_LEN,
     384          84 :                        ap_rep_blob.data,
     385             :                        ap_rep_blob.length);
     386             :         }
     387         118 :         memcpy(reply->data + HEADER_LEN + ap_rep_blob.length,
     388         118 :                enc_data_blob.data,
     389             :                enc_data_blob.length);
     390             : 
     391         118 :         rc = KDC_OK;
     392         118 : done:
     393         118 :         talloc_free(tmp_ctx);
     394         118 :         return rc;
     395             : }

Generated by: LCOV version 1.14