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

Generated by: LCOV version 1.13