LCOV - code coverage report
Current view: top level - source4/kdc - kpasswd-service.c (source / functions) Hit Total Coverage
Test: coverage report for recycleplus df22b230 Lines: 82 155 52.9 %
Date: 2024-02-14 10:14:15 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          38 : 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             :         uint16_t len;
      51             :         uint16_t verno;
      52             :         uint16_t ap_req_len;
      53             :         uint16_t enc_data_len;
      54          38 :         DATA_BLOB ap_req_blob = data_blob_null;
      55          38 :         DATA_BLOB ap_rep_blob = data_blob_null;
      56          38 :         DATA_BLOB enc_data_blob = data_blob_null;
      57          38 :         DATA_BLOB dec_data_blob = data_blob_null;
      58          38 :         DATA_BLOB kpasswd_dec_reply = data_blob_null;
      59          38 :         const char *error_string = NULL;
      60          38 :         krb5_error_code error_code = 0;
      61             :         struct cli_credentials *server_credentials;
      62             :         struct gensec_security *gensec_security;
      63             : #ifndef SAMBA4_USES_HEIMDAL
      64             :         struct sockaddr_storage remote_ss;
      65             : #endif
      66             :         struct sockaddr_storage local_ss;
      67             :         ssize_t socklen;
      68             :         TALLOC_CTX *tmp_ctx;
      69          38 :         kdc_code rc = KDC_ERROR;
      70          38 :         krb5_error_code code = 0;
      71             :         NTSTATUS status;
      72             :         int rv;
      73             :         bool is_inet;
      74             :         bool ok;
      75             : 
      76          38 :         if (kdc->am_rodc) {
      77           0 :                 return KDC_PROXY_REQUEST;
      78             :         }
      79             : 
      80          38 :         tmp_ctx = talloc_new(mem_ctx);
      81          38 :         if (tmp_ctx == NULL) {
      82           0 :                 return KDC_ERROR;
      83             :         }
      84             : 
      85          38 :         is_inet = tsocket_address_is_inet(remote_addr, "ip");
      86          38 :         if (!is_inet) {
      87           0 :                 DBG_WARNING("Invalid remote IP address");
      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           6 :         socklen = tsocket_address_bsd_sockaddr(remote_addr,
      99             :                                                (struct sockaddr *)&remote_ss,
     100             :                                                sizeof(struct sockaddr_storage));
     101           6 :         if (socklen < 0) {
     102           0 :                 DBG_WARNING("Invalid remote IP address");
     103           0 :                 goto done;
     104             :         }
     105             : #endif
     106             : 
     107             :         /* local_addr */
     108          38 :         socklen = tsocket_address_bsd_sockaddr(local_addr,
     109             :                                                (struct sockaddr *)&local_ss,
     110             :                                                sizeof(struct sockaddr_storage));
     111          38 :         if (socklen < 0) {
     112           0 :                 DBG_WARNING("Invalid local IP address");
     113           0 :                 goto done;
     114             :         }
     115             : 
     116          38 :         if (request->length <= HEADER_LEN) {
     117           0 :                 DBG_WARNING("Request truncated\n");
     118           0 :                 goto done;
     119             :         }
     120             : 
     121          38 :         len = RSVAL(request->data, 0);
     122          38 :         if (request->length != len) {
     123           0 :                 DBG_WARNING("Request length does not match\n");
     124           0 :                 goto done;
     125             :         }
     126             : 
     127          38 :         verno = RSVAL(request->data, 2);
     128          38 :         if (verno != 1 && verno != RFC3244_VERSION) {
     129           0 :                 DBG_WARNING("Unsupported version: 0x%04x\n", verno);
     130             :         }
     131             : 
     132          38 :         ap_req_len = RSVAL(request->data, 4);
     133          38 :         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          38 :         ap_req_blob = data_blob_const(&request->data[HEADER_LEN], ap_req_len);
     139             : 
     140          38 :         enc_data_len = len - ap_req_len;
     141          38 :         enc_data_blob = data_blob_const(&request->data[HEADER_LEN + ap_req_len],
     142             :                                         enc_data_len);
     143             : 
     144          38 :         server_credentials = cli_credentials_init(tmp_ctx);
     145          38 :         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          38 :         status = cli_credentials_set_krb5_context(server_credentials,
     158             :                                                   kdc->smb_krb5_context);
     159          38 :         if (!NT_STATUS_IS_OK(status)) {
     160           0 :                 goto done;
     161             :         }
     162             : 
     163          38 :         ok = cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
     164          38 :         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          38 :         ok = cli_credentials_set_realm(server_credentials,
     176          38 :                                        lpcfg_realm(kdc->task->lp_ctx),
     177             :                                        CRED_SPECIFIED);
     178          38 :         if (!ok) {
     179           0 :                 goto done;
     180             :         }
     181             : 
     182          38 :         ok = cli_credentials_set_username(server_credentials,
     183             :                                           "kadmin/changepw",
     184             :                                           CRED_SPECIFIED);
     185          38 :         if (!ok) {
     186           0 :                 goto done;
     187             :         }
     188             : 
     189             :         /* Check that the server principal is indeed CRED_SPECIFIED. */
     190             :         {
     191          38 :                 char *principal = NULL;
     192             :                 enum credentials_obtained obtained;
     193             : 
     194          38 :                 principal = cli_credentials_get_principal_and_obtained(server_credentials,
     195             :                                                                        tmp_ctx,
     196             :                                                                        &obtained);
     197          38 :                 if (obtained < CRED_SPECIFIED) {
     198           0 :                         goto done;
     199             :                 }
     200             : 
     201          38 :                 TALLOC_FREE(principal);
     202             :         }
     203             : 
     204          38 :         rv = cli_credentials_set_keytab_name(server_credentials,
     205          38 :                                              kdc->task->lp_ctx,
     206             :                                              kdc->kpasswd_keytab_name,
     207             :                                              CRED_SPECIFIED);
     208          38 :         if (rv != 0) {
     209           0 :                 DBG_ERR("Failed to set credentials keytab name\n");
     210           0 :                 goto done;
     211             :         }
     212             : 
     213          38 :         status = samba_server_gensec_start(tmp_ctx,
     214          38 :                                            kdc->task->event_ctx,
     215          38 :                                            kdc->task->msg_ctx,
     216          38 :                                            kdc->task->lp_ctx,
     217             :                                            server_credentials,
     218             :                                            "kpasswd",
     219             :                                            &gensec_security);
     220          38 :         if (!NT_STATUS_IS_OK(status)) {
     221           0 :                 goto done;
     222             :         }
     223             : 
     224          38 :         status = gensec_set_local_address(gensec_security, local_addr);
     225          38 :         if (!NT_STATUS_IS_OK(status)) {
     226           0 :                 goto done;
     227             :         }
     228             : 
     229             : #ifndef SAMBA4_USES_HEIMDAL
     230           6 :         status = gensec_set_remote_address(gensec_security, remote_addr);
     231           6 :         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          38 :         gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL);
     238             : 
     239             :         /* Use the krb5 gesec mechanism so we can load DB modules */
     240          38 :         status = gensec_start_mech_by_name(gensec_security, "krb5");
     241          38 :         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          38 :         status = gensec_update(gensec_security, tmp_ctx,
     258             :                                ap_req_blob, &ap_rep_blob);
     259          38 :         if (!NT_STATUS_IS_OK(status) &&
     260           0 :             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
     261           0 :                 ap_rep_blob = data_blob_null;
     262           0 :                 error_code = KRB5_KPASSWD_HARDERROR;
     263           0 :                 error_string = talloc_asprintf(tmp_ctx,
     264             :                                                "gensec_update failed - %s\n",
     265             :                                                nt_errstr(status));
     266           0 :                 DBG_ERR("%s", error_string);
     267           0 :                 goto reply;
     268             :         }
     269             : 
     270          38 :         status = gensec_unwrap(gensec_security,
     271             :                                tmp_ctx,
     272             :                                &enc_data_blob,
     273             :                                &dec_data_blob);
     274          38 :         if (!NT_STATUS_IS_OK(status)) {
     275           0 :                 ap_rep_blob = data_blob_null;
     276           0 :                 error_code = KRB5_KPASSWD_HARDERROR;
     277           0 :                 error_string = talloc_asprintf(tmp_ctx,
     278             :                                                "gensec_unwrap failed - %s\n",
     279             :                                                nt_errstr(status));
     280           0 :                 DBG_ERR("%s", error_string);
     281           0 :                 goto reply;
     282             :         }
     283             : 
     284          38 :         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          38 :         if (code != 0) {
     292           0 :                 ap_rep_blob = data_blob_null;
     293           0 :                 error_code = code;
     294           0 :                 goto reply;
     295             :         }
     296             : 
     297          38 :         status = gensec_wrap(gensec_security,
     298             :                              tmp_ctx,
     299             :                              &kpasswd_dec_reply,
     300             :                              &enc_data_blob);
     301          38 :         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          38 : reply:
     312          38 :         if (error_code != 0) {
     313             :                 krb5_data k_enc_data;
     314             :                 krb5_data k_dec_data;
     315             :                 const char *principal_string;
     316             :                 krb5_principal server_principal;
     317             : 
     318           0 :                 if (error_string == NULL) {
     319           0 :                         DBG_ERR("Invalid error string! This should not happen\n");
     320           0 :                         goto done;
     321             :                 }
     322             : 
     323           0 :                 ok = kpasswd_make_error_reply(tmp_ctx,
     324             :                                               error_code,
     325             :                                               error_string,
     326             :                                               &dec_data_blob);
     327           0 :                 if (!ok) {
     328           0 :                         DBG_ERR("Failed to create error reply\n");
     329           0 :                         goto done;
     330             :                 }
     331             : 
     332           0 :                 k_dec_data = smb_krb5_data_from_blob(dec_data_blob);
     333             : 
     334           0 :                 principal_string = cli_credentials_get_principal(server_credentials,
     335             :                                                                  tmp_ctx);
     336           0 :                 if (principal_string == NULL) {
     337           0 :                         goto done;
     338             :                 }
     339             : 
     340           0 :                 code = smb_krb5_parse_name(kdc->smb_krb5_context->krb5_context,
     341             :                                            principal_string,
     342             :                                            &server_principal);
     343           0 :                 if (code != 0) {
     344           0 :                         DBG_ERR("Failed to create principal: %s\n",
     345             :                                 error_message(code));
     346           0 :                         goto done;
     347             :                 }
     348             : 
     349           0 :                 code = smb_krb5_mk_error(kdc->smb_krb5_context->krb5_context,
     350           0 :                                          KRB5KDC_ERR_NONE + error_code,
     351             :                                          NULL, /* e_text */
     352             :                                          &k_dec_data,
     353             :                                          NULL, /* client */
     354             :                                          server_principal,
     355             :                                          &k_enc_data);
     356           0 :                 krb5_free_principal(kdc->smb_krb5_context->krb5_context,
     357             :                                     server_principal);
     358           0 :                 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           0 :                 enc_data_blob = data_blob_talloc(tmp_ctx,
     365             :                                                  k_enc_data.data,
     366             :                                                  k_enc_data.length);
     367           0 :                 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          38 :         *reply = data_blob_talloc(mem_ctx,
     374             :                                   NULL,
     375             :                                   HEADER_LEN + ap_rep_blob.length + enc_data_blob.length);
     376          38 :         if (reply->data == NULL) {
     377           0 :                 goto done;
     378             :         }
     379          38 :         RSSVAL(reply->data, 0, reply->length);
     380          38 :         RSSVAL(reply->data, 2, 1);
     381          38 :         RSSVAL(reply->data, 4, ap_rep_blob.length);
     382          38 :         memcpy(reply->data + HEADER_LEN,
     383          38 :                ap_rep_blob.data,
     384             :                ap_rep_blob.length);
     385          38 :         memcpy(reply->data + HEADER_LEN + ap_rep_blob.length,
     386          38 :                enc_data_blob.data,
     387             :                enc_data_blob.length);
     388             : 
     389          38 :         rc = KDC_OK;
     390          38 : done:
     391          38 :         talloc_free(tmp_ctx);
     392          38 :         return rc;
     393             : }

Generated by: LCOV version 1.14