LCOV - code coverage report
Current view: top level - source3/libads - krb5_setpw.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 73 156 46.8 %
Date: 2024-04-21 15:09:00 Functions: 3 5 60.0 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             :    krb5 set password implementation
       4             :    Copyright (C) Andrew Tridgell 2001
       5             :    Copyright (C) Remus Koos 2001 (remuskoos@yahoo.com)
       6             :    
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             :    
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             :    
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "smb_krb5.h"
      23             : #include "libads/kerberos_proto.h"
      24             : #include "../lib/util/asn1.h"
      25             : 
      26             : #ifdef HAVE_KRB5
      27             : 
      28             : /* Those are defined by kerberos-set-passwd-02.txt and are probably
      29             :  * not supported by M$ implementation */
      30             : #define KRB5_KPASSWD_POLICY_REJECT              8
      31             : #define KRB5_KPASSWD_BAD_PRINCIPAL              9
      32             : #define KRB5_KPASSWD_ETYPE_NOSUPP               10
      33             : 
      34             : /*
      35             :  * we've got to be able to distinguish KRB_ERRORs from other
      36             :  * requests - valid response for CHPW v2 replies.
      37             :  */
      38             : 
      39           0 : static krb5_error_code kpasswd_err_to_krb5_err(krb5_error_code res_code)
      40             : {
      41           0 :         switch (res_code) {
      42           0 :         case KRB5_KPASSWD_ACCESSDENIED:
      43           0 :                 return KRB5KDC_ERR_BADOPTION;
      44           0 :         case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
      45           0 :                 return KRB5KDC_ERR_BADOPTION;
      46             :                 /* return KV5M_ALT_METHOD; MIT-only define */
      47           0 :         case KRB5_KPASSWD_ETYPE_NOSUPP:
      48           0 :                 return KRB5KDC_ERR_ETYPE_NOSUPP;
      49           0 :         case KRB5_KPASSWD_BAD_PRINCIPAL:
      50           0 :                 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
      51           0 :         case KRB5_KPASSWD_POLICY_REJECT:
      52             :         case KRB5_KPASSWD_SOFTERROR:
      53           0 :                 return KRB5KDC_ERR_POLICY;
      54           0 :         default:
      55           0 :                 return KRB5KRB_ERR_GENERIC;
      56             :         }
      57             : }
      58             : 
      59           6 : ADS_STATUS ads_krb5_set_password(const char *kdc_host, const char *principal,
      60             :                                  const char *newpw, int time_offset)
      61             : {
      62             : 
      63           0 :         ADS_STATUS aret;
      64           6 :         krb5_error_code ret = 0;
      65           6 :         krb5_context context = NULL;
      66           6 :         krb5_principal princ = NULL;
      67           6 :         krb5_ccache ccache = NULL;
      68           0 :         int result_code;
      69           6 :         krb5_data result_code_string = { 0 };
      70           6 :         krb5_data result_string = { 0 };
      71             : 
      72           6 :         ret = smb_krb5_init_context_common(&context);
      73           6 :         if (ret) {
      74           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
      75             :                         error_message(ret));
      76           0 :                 return ADS_ERROR_KRB5(ret);
      77             :         }
      78             : 
      79           6 :         if (principal) {
      80           6 :                 ret = smb_krb5_parse_name(context, principal, &princ);
      81           6 :                 if (ret) {
      82           0 :                         krb5_free_context(context);
      83           0 :                         DEBUG(1, ("Failed to parse %s (%s)\n", principal,
      84             :                                   error_message(ret)));
      85           0 :                         return ADS_ERROR_KRB5(ret);
      86             :                 }
      87             :         }
      88             : 
      89           6 :         if (time_offset != 0) {
      90           0 :                 krb5_set_real_time(context, time(NULL) + time_offset, 0);
      91             :         }
      92             : 
      93           6 :         ret = krb5_cc_default(context, &ccache);
      94           6 :         if (ret) {
      95           0 :                 krb5_free_principal(context, princ);
      96           0 :                 krb5_free_context(context);
      97           0 :                 DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret)));
      98           0 :                 return ADS_ERROR_KRB5(ret);
      99             :         }
     100             : 
     101           6 :         ret = krb5_set_password_using_ccache(context,
     102             :                                              ccache,
     103             :                                              discard_const_p(char, newpw),
     104             :                                              princ,
     105             :                                              &result_code,
     106             :                                              &result_code_string,
     107             :                                              &result_string);
     108           6 :         if (ret) {
     109           0 :                 DEBUG(1, ("krb5_set_password failed (%s)\n", error_message(ret)));
     110           0 :                 aret = ADS_ERROR_KRB5(ret);
     111           0 :                 goto done;
     112             :         }
     113             : 
     114           6 :         if (result_code != KRB5_KPASSWD_SUCCESS) {
     115           0 :                 ret = kpasswd_err_to_krb5_err(result_code);
     116           0 :                 DEBUG(1, ("krb5_set_password failed (%s)\n", error_message(ret)));
     117           0 :                 aret = ADS_ERROR_KRB5(ret);
     118           0 :                 goto done;
     119             :         }
     120             : 
     121           6 :         aret = ADS_SUCCESS;
     122             : 
     123           6 :  done:
     124           6 :         smb_krb5_free_data_contents(context, &result_code_string);
     125           6 :         smb_krb5_free_data_contents(context, &result_string);
     126           6 :         krb5_free_principal(context, princ);
     127           6 :         krb5_cc_close(context, ccache);
     128           6 :         krb5_free_context(context);
     129             : 
     130           6 :         return aret;
     131             : }
     132             : 
     133             : /*
     134             :   we use a prompter to avoid a crash bug in the kerberos libs when 
     135             :   dealing with empty passwords
     136             :   this prompter is just a string copy ...
     137             : */
     138             : static krb5_error_code 
     139           0 : kerb_prompter(krb5_context ctx, void *data,
     140             :                const char *name,
     141             :                const char *banner,
     142             :                int num_prompts,
     143             :                krb5_prompt prompts[])
     144             : {
     145           0 :         if (num_prompts == 0) return 0;
     146             : 
     147           0 :         memset(prompts[0].reply->data, 0, prompts[0].reply->length);
     148           0 :         if (prompts[0].reply->length > 0) {
     149           0 :                 if (data) {
     150           0 :                         strncpy((char *)prompts[0].reply->data,
     151             :                                 (const char *)data,
     152           0 :                                 prompts[0].reply->length-1);
     153           0 :                         prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
     154             :                 } else {
     155           0 :                         prompts[0].reply->length = 0;
     156             :                 }
     157             :         }
     158           0 :         return 0;
     159             : }
     160             : 
     161           6 : static ADS_STATUS ads_krb5_chg_password(const char *kdc_host,
     162             :                                         const char *principal,
     163             :                                         const char *oldpw,
     164             :                                         const char *newpw,
     165             :                                         int time_offset)
     166             : {
     167           0 :         ADS_STATUS aret;
     168           0 :         krb5_error_code ret;
     169           6 :         krb5_context context = NULL;
     170           0 :         krb5_principal princ;
     171           6 :         krb5_get_init_creds_opt *opts = NULL;
     172           0 :         krb5_creds creds;
     173           6 :         char *chpw_princ = NULL, *password;
     174           6 :         char *realm = NULL;
     175           0 :         int result_code;
     176           6 :         krb5_data result_code_string = { 0 };
     177           6 :         krb5_data result_string = { 0 };
     178           6 :         smb_krb5_addresses *addr = NULL;
     179             : 
     180           6 :         ret = smb_krb5_init_context_common(&context);
     181           6 :         if (ret) {
     182           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     183             :                         error_message(ret));
     184           0 :                 return ADS_ERROR_KRB5(ret);
     185             :         }
     186             : 
     187           6 :         if ((ret = smb_krb5_parse_name(context, principal, &princ))) {
     188           0 :                 krb5_free_context(context);
     189           0 :                 DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret)));
     190           0 :                 return ADS_ERROR_KRB5(ret);
     191             :         }
     192             : 
     193           6 :         ret = krb5_get_init_creds_opt_alloc(context, &opts);
     194           6 :         if (ret != 0) {
     195           0 :                 krb5_free_context(context);
     196           0 :                 DBG_WARNING("krb5_get_init_creds_opt_alloc failed: %s\n",
     197             :                             error_message(ret));
     198           0 :                 return ADS_ERROR_KRB5(ret);
     199             :         }
     200             : 
     201           6 :         krb5_get_init_creds_opt_set_tkt_life(opts, 5 * 60);
     202           6 :         krb5_get_init_creds_opt_set_renew_life(opts, 0);
     203           6 :         krb5_get_init_creds_opt_set_forwardable(opts, 0);
     204           6 :         krb5_get_init_creds_opt_set_proxiable(opts, 0);
     205             : #ifdef SAMBA4_USES_HEIMDAL
     206           3 :         krb5_get_init_creds_opt_set_win2k(context, opts, true);
     207           3 :         krb5_get_init_creds_opt_set_canonicalize(context, opts, true);
     208             : #else /* MIT */
     209             : #if 0
     210             :         /*
     211             :          * FIXME
     212             :          *
     213             :          * Due to an upstream MIT Kerberos bug, this feature is not
     214             :          * not working. Affection versions (2019-10-09): <= 1.17
     215             :          *
     216             :          * Reproducer:
     217             :          * kinit -C aDmInIsTrAtOr@ACME.COM -S kadmin/changepw@ACME.COM
     218             :          *
     219             :          * This is NOT a problem if the service is a krbtgt.
     220             :          *
     221             :          * https://bugzilla.samba.org/show_bug.cgi?id=14155
     222             :          */
     223             :         krb5_get_init_creds_opt_set_canonicalize(opts, true);
     224             : #endif
     225             : #endif /* MIT */
     226             : 
     227             :         /* note that heimdal will fill in the local addresses if the addresses
     228             :          * in the creds_init_opt are all empty and then later fail with invalid
     229             :          * address, sending our local netbios krb5 address - just like windows
     230             :          * - avoids this - gd */
     231           6 :         ret = smb_krb5_gen_netbios_krb5_address(&addr, lp_netbios_name());
     232           6 :         if (ret) {
     233           0 :                 krb5_free_principal(context, princ);
     234           0 :                 krb5_get_init_creds_opt_free(context, opts);
     235           0 :                 krb5_free_context(context);
     236           0 :                 return ADS_ERROR_KRB5(ret);
     237             :         }
     238           6 :         krb5_get_init_creds_opt_set_address_list(opts, addr->addrs);
     239             : 
     240           6 :         realm = smb_krb5_principal_get_realm(NULL, context, princ);
     241             : 
     242             :         /* We have to obtain an INITIAL changepw ticket for changing password */
     243           6 :         if (asprintf(&chpw_princ, "kadmin/changepw@%s", realm) == -1) {
     244           0 :                 krb5_free_principal(context, princ);
     245           0 :                 krb5_get_init_creds_opt_free(context, opts);
     246           0 :                 smb_krb5_free_addresses(context, addr);
     247           0 :                 krb5_free_context(context);
     248           0 :                 TALLOC_FREE(realm);
     249           0 :                 DEBUG(1, ("ads_krb5_chg_password: asprintf fail\n"));
     250           0 :                 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
     251             :         }
     252             : 
     253           6 :         TALLOC_FREE(realm);
     254           6 :         password = SMB_STRDUP(oldpw);
     255           6 :         ret = krb5_get_init_creds_password(context, &creds, princ, password,
     256             :                                            kerb_prompter, NULL,
     257             :                                            0, chpw_princ, opts);
     258           6 :         krb5_get_init_creds_opt_free(context, opts);
     259           6 :         smb_krb5_free_addresses(context, addr);
     260           6 :         SAFE_FREE(chpw_princ);
     261           6 :         SAFE_FREE(password);
     262             : 
     263           6 :         if (ret) {
     264           0 :                 if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
     265           0 :                         DEBUG(1,("Password incorrect while getting initial ticket\n"));
     266             :                 } else {
     267           0 :                         DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret)));
     268             :                 }
     269           0 :                 krb5_free_principal(context, princ);
     270           0 :                 krb5_free_context(context);
     271           0 :                 return ADS_ERROR_KRB5(ret);
     272             :         }
     273             : 
     274           6 :         ret = krb5_set_password(context,
     275             :                                 &creds,
     276             :                                 discard_const_p(char, newpw),
     277             :                                 NULL,
     278             :                                 &result_code,
     279             :                                 &result_code_string,
     280             :                                 &result_string);
     281             : 
     282           6 :         if (ret) {
     283           0 :                 DEBUG(1, ("krb5_change_password failed (%s)\n", error_message(ret)));
     284           0 :                 aret = ADS_ERROR_KRB5(ret);
     285           0 :                 goto done;
     286             :         }
     287             : 
     288           6 :         if (result_code != KRB5_KPASSWD_SUCCESS) {
     289           0 :                 ret = kpasswd_err_to_krb5_err(result_code);
     290           0 :                 DEBUG(1, ("krb5_change_password failed (%s)\n", error_message(ret)));
     291           0 :                 aret = ADS_ERROR_KRB5(ret);
     292           0 :                 goto done;
     293             :         }
     294             : 
     295           6 :         aret = ADS_SUCCESS;
     296             : 
     297           6 :  done:
     298           6 :         smb_krb5_free_data_contents(context, &result_code_string);
     299           6 :         smb_krb5_free_data_contents(context, &result_string);
     300           6 :         krb5_free_principal(context, princ);
     301           6 :         krb5_free_context(context);
     302             : 
     303           6 :         return aret;
     304             : }
     305             : 
     306           8 : ADS_STATUS kerberos_set_password(const char *kpasswd_server,
     307             :                                  const char *auth_principal,
     308             :                                  const char *auth_password,
     309             :                                  const char *target_principal,
     310             :                                  const char *new_password, int time_offset)
     311             : {
     312           0 :         int ret;
     313             : 
     314           8 :         if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset, NULL))) {
     315           0 :                 DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal, error_message(ret)));
     316           0 :                 return ADS_ERROR_KRB5(ret);
     317             :         }
     318             : 
     319           8 :         if (!strcmp(auth_principal, target_principal)) {
     320           6 :                 return ads_krb5_chg_password(kpasswd_server, target_principal,
     321             :                                              auth_password, new_password,
     322             :                                              time_offset);
     323             :         } else {
     324           2 :                 return ads_krb5_set_password(kpasswd_server, target_principal,
     325             :                                              new_password, time_offset);
     326             :         }
     327             : }
     328             : 
     329             : #endif

Generated by: LCOV version 1.14