LCOV - code coverage report
Current view: top level - source4/kdc - kpasswd-service-mit.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 105 146 71.9 %
Date: 2024-05-31 13:13:24 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Samba kpasswd implementation
       5             : 
       6             :    Copyright (c) 2016      Andreas Schneider <asn@samba.org>
       7             : 
       8             :    This program is free software; you can redistribute it and/or modify
       9             :    it under the terms of the GNU General Public License as published by
      10             :    the Free Software Foundation; either version 3 of the License, or
      11             :    (at your option) any later version.
      12             : 
      13             :    This program is distributed in the hope that it will be useful,
      14             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             :    GNU General Public License for more details.
      17             : 
      18             :    You should have received a copy of the GNU General Public License
      19             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      20             : */
      21             : 
      22             : #include "includes.h"
      23             : #include "samba/service_task.h"
      24             : #include "param/param.h"
      25             : #include "auth/auth.h"
      26             : #include "auth/gensec/gensec.h"
      27             : #include "gensec_krb5_helpers.h"
      28             : #include "kdc/kdc-server.h"
      29             : #include "kdc/kpasswd_glue.h"
      30             : #include "kdc/kpasswd-service.h"
      31             : #include "kdc/kpasswd-helper.h"
      32             : #include "../lib/util/asn1.h"
      33             : 
      34             : #undef DBGC_CLASS
      35             : #define DBGC_CLASS DBGC_KERBEROS
      36             : 
      37             : #define RFC3244_VERSION 0xff80
      38             : 
      39             : krb5_error_code decode_krb5_setpw_req(const krb5_data *code,
      40             :                                       krb5_data **password_out,
      41             :                                       krb5_principal *target_out);
      42             : 
      43             : /*
      44             :  * A fallback for when MIT refuses to parse a setpw structure without the
      45             :  * (optional) target principal and realm
      46             :  */
      47          10 : static bool decode_krb5_setpw_req_simple(TALLOC_CTX *mem_ctx,
      48             :                                          const DATA_BLOB *decoded_data,
      49             :                                          DATA_BLOB *clear_data)
      50             : {
      51          10 :         struct asn1_data *asn1 = NULL;
      52             :         bool ret;
      53             : 
      54          10 :         asn1 = asn1_init(mem_ctx, 3);
      55          10 :         if (asn1 == NULL) {
      56           0 :                 return false;
      57             :         }
      58             : 
      59          10 :         ret = asn1_load(asn1, *decoded_data);
      60          10 :         if (!ret) {
      61           0 :                 goto out;
      62             :         }
      63             : 
      64          10 :         ret = asn1_start_tag(asn1, ASN1_SEQUENCE(0));
      65          10 :         if (!ret) {
      66           0 :                 goto out;
      67             :         }
      68          10 :         ret = asn1_start_tag(asn1, ASN1_CONTEXT(0));
      69          10 :         if (!ret) {
      70           0 :                 goto out;
      71             :         }
      72          10 :         ret = asn1_read_OctetString(asn1, mem_ctx, clear_data);
      73          10 :         if (!ret) {
      74           0 :                 goto out;
      75             :         }
      76             : 
      77          10 :         ret = asn1_end_tag(asn1);
      78          10 :         if (!ret) {
      79           0 :                 goto out;
      80             :         }
      81          10 :         ret = asn1_end_tag(asn1);
      82             : 
      83          10 : out:
      84          10 :         asn1_free(asn1);
      85             : 
      86          10 :         return ret;
      87             : }
      88             : 
      89          28 : static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
      90             :                                                TALLOC_CTX *mem_ctx,
      91             :                                                const struct gensec_security *gensec_security,
      92             :                                                struct auth_session_info *session_info,
      93             :                                                DATA_BLOB *password,
      94             :                                                DATA_BLOB *kpasswd_reply,
      95             :                                                const char **error_string)
      96             : {
      97             :         NTSTATUS status;
      98          28 :         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
      99             :         enum samPwdChangeReason reject_reason;
     100          28 :         const char *reject_string = NULL;
     101             :         struct samr_DomInfo1 *dominfo;
     102             :         bool ok;
     103             :         int ret;
     104             : 
     105             :         /*
     106             :          * We're doing a password change (rather than a password set), so check
     107             :          * that we were given an initial ticket.
     108             :          */
     109          28 :         ret = gensec_krb5_initial_ticket(gensec_security);
     110          28 :         if (ret != 1) {
     111           2 :                 *error_string = "Expected an initial ticket";
     112           2 :                 return KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
     113             :         }
     114             : 
     115          26 :         status = samdb_kpasswd_change_password(mem_ctx,
     116          26 :                                                kdc->task->lp_ctx,
     117          26 :                                                kdc->task->event_ctx,
     118             :                                                session_info,
     119             :                                                password,
     120             :                                                &reject_reason,
     121             :                                                &dominfo,
     122             :                                                &reject_string,
     123             :                                                &result);
     124          26 :         if (!NT_STATUS_IS_OK(status)) {
     125           0 :                 ok = kpasswd_make_error_reply(mem_ctx,
     126             :                                               KRB5_KPASSWD_ACCESSDENIED,
     127             :                                               reject_string,
     128             :                                               kpasswd_reply);
     129           0 :                 if (!ok) {
     130           0 :                         *error_string = "Failed to create reply";
     131           0 :                         return KRB5_KPASSWD_HARDERROR;
     132             :                 }
     133             :                 /* We want to send an an authenticated packet. */
     134           0 :                 return 0;
     135             :         }
     136             : 
     137          26 :         ok = kpasswd_make_pwchange_reply(mem_ctx,
     138             :                                          result,
     139             :                                          reject_reason,
     140             :                                          dominfo,
     141             :                                          kpasswd_reply);
     142          26 :         if (!ok) {
     143           0 :                 *error_string = "Failed to create reply";
     144           0 :                 return KRB5_KPASSWD_HARDERROR;
     145             :         }
     146             : 
     147          26 :         return 0;
     148             : }
     149             : 
     150          15 : static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
     151             :                                             TALLOC_CTX *mem_ctx,
     152             :                                             const struct gensec_security *gensec_security,
     153             :                                             struct auth_session_info *session_info,
     154             :                                             DATA_BLOB *decoded_data,
     155             :                                             DATA_BLOB *kpasswd_reply,
     156             :                                             const char **error_string)
     157             : {
     158          15 :         krb5_context context = kdc->smb_krb5_context->krb5_context;
     159             :         DATA_BLOB clear_data;
     160             :         krb5_data k_dec_data;
     161          15 :         krb5_data *k_clear_data = NULL;
     162          15 :         krb5_principal target_principal = NULL;
     163             :         krb5_error_code code;
     164             :         DATA_BLOB password;
     165          15 :         char *target_realm = NULL;
     166          15 :         char *target_name = NULL;
     167          15 :         char *target_principal_string = NULL;
     168          15 :         bool is_service_principal = false;
     169             :         bool ok;
     170             :         size_t num_components;
     171          15 :         enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR;
     172          15 :         struct samr_DomInfo1 *dominfo = NULL;
     173             :         NTSTATUS status;
     174             : 
     175          15 :         k_dec_data = smb_krb5_data_from_blob(*decoded_data);
     176             : 
     177          15 :         code = decode_krb5_setpw_req(&k_dec_data,
     178             :                                      &k_clear_data,
     179             :                                      &target_principal);
     180          15 :         if (code == 0) {
     181           5 :                 clear_data.data = (uint8_t *)k_clear_data->data;
     182           5 :                 clear_data.length = k_clear_data->length;
     183             :         } else {
     184          10 :                 target_principal = NULL;
     185             : 
     186             :                 /*
     187             :                  * The MIT decode failed, so fall back to trying the simple
     188             :                  * case, without target_principal.
     189             :                  */
     190          10 :                 ok = decode_krb5_setpw_req_simple(mem_ctx,
     191             :                                                   decoded_data,
     192             :                                                   &clear_data);
     193          10 :                 if (!ok) {
     194           2 :                         DBG_WARNING("decode_krb5_setpw_req failed: %s\n",
     195             :                                     error_message(code));
     196           2 :                         ok = kpasswd_make_error_reply(mem_ctx,
     197             :                                                       KRB5_KPASSWD_MALFORMED,
     198             :                                                       "Failed to decode packet",
     199             :                                                       kpasswd_reply);
     200           2 :                         if (!ok) {
     201           0 :                                 *error_string = "Failed to create reply";
     202           0 :                                 return KRB5_KPASSWD_HARDERROR;
     203             :                         }
     204           2 :                         return 0;
     205             :                 }
     206             :         }
     207             : 
     208          13 :         ok = convert_string_talloc_handle(mem_ctx,
     209          13 :                                           lpcfg_iconv_handle(kdc->task->lp_ctx),
     210             :                                           CH_UTF8,
     211             :                                           CH_UTF16,
     212          13 :                                           clear_data.data,
     213             :                                           clear_data.length,
     214             :                                           &password.data,
     215             :                                           &password.length);
     216          13 :         if (k_clear_data != NULL) {
     217           5 :                 krb5_free_data(context, k_clear_data);
     218             :         }
     219          13 :         if (!ok) {
     220           0 :                 DBG_WARNING("String conversion failed\n");
     221           0 :                 *error_string = "String conversion failed";
     222           0 :                 return KRB5_KPASSWD_HARDERROR;
     223             :         }
     224             : 
     225          13 :         if (target_principal != NULL) {
     226           5 :                 target_realm = smb_krb5_principal_get_realm(
     227             :                         mem_ctx, context, target_principal);
     228           5 :                 code = krb5_unparse_name_flags(context,
     229             :                                                target_principal,
     230             :                                                KRB5_PRINCIPAL_UNPARSE_NO_REALM,
     231             :                                                &target_name);
     232           5 :                 if (code != 0) {
     233           0 :                         DBG_WARNING("Failed to parse principal\n");
     234           0 :                         *error_string = "String conversion failed";
     235           0 :                         return KRB5_KPASSWD_HARDERROR;
     236             :                 }
     237             :         }
     238             : 
     239          13 :         if ((target_name != NULL && target_realm == NULL) ||
     240          13 :             (target_name == NULL && target_realm != NULL)) {
     241           0 :                 krb5_free_principal(context, target_principal);
     242           0 :                 TALLOC_FREE(target_realm);
     243           0 :                 SAFE_FREE(target_name);
     244             : 
     245           0 :                 ok = kpasswd_make_error_reply(mem_ctx,
     246             :                                               KRB5_KPASSWD_MALFORMED,
     247             :                                               "Realm and principal must be "
     248             :                                               "both present, or neither "
     249             :                                               "present",
     250             :                                               kpasswd_reply);
     251           0 :                 if (!ok) {
     252           0 :                         *error_string = "Failed to create reply";
     253           0 :                         return KRB5_KPASSWD_HARDERROR;
     254             :                 }
     255           0 :                 return 0;
     256             :         }
     257             : 
     258          13 :         if (target_name != NULL && target_realm != NULL) {
     259           5 :                 TALLOC_FREE(target_realm);
     260           5 :                 SAFE_FREE(target_name);
     261             :         } else {
     262           8 :                 krb5_free_principal(context, target_principal);
     263           8 :                 TALLOC_FREE(target_realm);
     264           8 :                 SAFE_FREE(target_name);
     265             : 
     266           8 :                 return kpasswd_change_password(kdc,
     267             :                                                mem_ctx,
     268             :                                                gensec_security,
     269             :                                                session_info,
     270             :                                                &password,
     271             :                                                kpasswd_reply,
     272             :                                                error_string);
     273             :         }
     274             : 
     275           5 :         num_components = krb5_princ_size(context, target_principal);
     276           5 :         if (num_components >= 2) {
     277           0 :                 is_service_principal = true;
     278           0 :                 code = krb5_unparse_name_flags(context,
     279             :                                                target_principal,
     280             :                                                KRB5_PRINCIPAL_UNPARSE_SHORT,
     281             :                                                &target_principal_string);
     282             :         } else {
     283           5 :                 code = krb5_unparse_name(context,
     284             :                                          target_principal,
     285             :                                          &target_principal_string);
     286             :         }
     287           5 :         krb5_free_principal(context, target_principal);
     288           5 :         if (code != 0) {
     289           0 :                 ok = kpasswd_make_error_reply(mem_ctx,
     290             :                                               KRB5_KPASSWD_MALFORMED,
     291             :                                               "Failed to parse principal",
     292             :                                               kpasswd_reply);
     293           0 :                 if (!ok) {
     294           0 :                         *error_string = "Failed to create reply";
     295           0 :                         return KRB5_KPASSWD_HARDERROR;
     296             :                 }
     297             :         }
     298             : 
     299           5 :         status = kpasswd_samdb_set_password(mem_ctx,
     300           5 :                                             kdc->task->event_ctx,
     301           5 :                                             kdc->task->lp_ctx,
     302             :                                             session_info,
     303             :                                             is_service_principal,
     304             :                                             target_principal_string,
     305             :                                             &password,
     306             :                                             &reject_reason,
     307             :                                             &dominfo);
     308           5 :         if (!NT_STATUS_IS_OK(status)) {
     309           1 :                 DBG_ERR("kpasswd_samdb_set_password failed - %s\n",
     310             :                         nt_errstr(status));
     311             :         }
     312             : 
     313           5 :         ok = kpasswd_make_pwchange_reply(mem_ctx,
     314             :                                          status,
     315             :                                          reject_reason,
     316             :                                          dominfo,
     317             :                                          kpasswd_reply);
     318           5 :         if (!ok) {
     319           0 :                 *error_string = "Failed to create reply";
     320           0 :                 return KRB5_KPASSWD_HARDERROR;
     321             :         }
     322             : 
     323           5 :         return 0;
     324             : }
     325             : 
     326          40 : krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
     327             :                                        TALLOC_CTX *mem_ctx,
     328             :                                        struct gensec_security *gensec_security,
     329             :                                        uint16_t verno,
     330             :                                        DATA_BLOB *decoded_data,
     331             :                                        DATA_BLOB *kpasswd_reply,
     332             :                                        const char **error_string)
     333             : {
     334             :         struct auth_session_info *session_info;
     335             :         NTSTATUS status;
     336             :         krb5_error_code code;
     337             : 
     338          40 :         status = gensec_session_info(gensec_security,
     339             :                                      mem_ctx,
     340             :                                      &session_info);
     341          40 :         if (!NT_STATUS_IS_OK(status)) {
     342           0 :                 *error_string = talloc_asprintf(mem_ctx,
     343             :                                                 "gensec_session_info failed - "
     344             :                                                 "%s",
     345             :                                                 nt_errstr(status));
     346           0 :                 return KRB5_KPASSWD_HARDERROR;
     347             :         }
     348             : 
     349             :         /*
     350             :          * Since the kpasswd service shares its keys with the krbtgt, we might
     351             :          * have received a TGT rather than a kpasswd ticket. We need to check
     352             :          * the ticket type to ensure that TGTs cannot be misused in this manner.
     353             :          */
     354          40 :         code = kpasswd_check_non_tgt(session_info,
     355             :                                      error_string);
     356          40 :         if (code != 0) {
     357           4 :                 DBG_WARNING("%s\n", *error_string);
     358           4 :                 return code;
     359             :         }
     360             : 
     361          36 :         switch(verno) {
     362          21 :         case 1: {
     363             :                 DATA_BLOB password;
     364             :                 bool ok;
     365             : 
     366          21 :                 ok = convert_string_talloc_handle(mem_ctx,
     367          21 :                                                   lpcfg_iconv_handle(kdc->task->lp_ctx),
     368             :                                                   CH_UTF8,
     369             :                                                   CH_UTF16,
     370          21 :                                                   decoded_data->data,
     371             :                                                   decoded_data->length,
     372             :                                                   &password.data,
     373             :                                                   &password.length);
     374          21 :                 if (!ok) {
     375           1 :                         *error_string = "String conversion failed!";
     376           1 :                         DBG_WARNING("%s\n", *error_string);
     377           1 :                         return KRB5_KPASSWD_HARDERROR;
     378             :                 }
     379             : 
     380          20 :                 return kpasswd_change_password(kdc,
     381             :                                                mem_ctx,
     382             :                                                gensec_security,
     383             :                                                session_info,
     384             :                                                &password,
     385             :                                                kpasswd_reply,
     386             :                                                error_string);
     387             :         }
     388          15 :         case RFC3244_VERSION: {
     389          15 :                 return kpasswd_set_password(kdc,
     390             :                                             mem_ctx,
     391             :                                             gensec_security,
     392             :                                             session_info,
     393             :                                             decoded_data,
     394             :                                             kpasswd_reply,
     395             :                                             error_string);
     396             :         }
     397           0 :         default:
     398           0 :                 return KRB5_KPASSWD_BAD_VERSION;
     399             :         }
     400             : 
     401             :         return 0;
     402             : }

Generated by: LCOV version 1.14