LCOV - code coverage report
Current view: top level - source3/libads - kerberos_keytab.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 302 509 59.3 %
Date: 2023-11-21 12:31:41 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    kerberos keytab utility library
       4             :    Copyright (C) Andrew Tridgell 2001
       5             :    Copyright (C) Remus Koos 2001
       6             :    Copyright (C) Luke Howard 2003
       7             :    Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
       8             :    Copyright (C) Guenther Deschner 2003
       9             :    Copyright (C) Rakesh Patel 2004
      10             :    Copyright (C) Dan Perry 2004
      11             :    Copyright (C) Jeremy Allison 2004
      12             :    Copyright (C) Gerald Carter 2006
      13             : 
      14             :    This program is free software; you can redistribute it and/or modify
      15             :    it under the terms of the GNU General Public License as published by
      16             :    the Free Software Foundation; either version 3 of the License, or
      17             :    (at your option) any later version.
      18             : 
      19             :    This program is distributed in the hope that it will be useful,
      20             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      21             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      22             :    GNU General Public License for more details.
      23             : 
      24             :    You should have received a copy of the GNU General Public License
      25             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      26             : */
      27             : 
      28             : #include "includes.h"
      29             : #include "smb_krb5.h"
      30             : #include "ads.h"
      31             : #include "secrets.h"
      32             : 
      33             : #ifdef HAVE_KRB5
      34             : 
      35             : #ifdef HAVE_ADS
      36             : 
      37             : /* This MAX_NAME_LEN is a constant defined in krb5.h */
      38             : #ifndef MAX_KEYTAB_NAME_LEN
      39             : #define MAX_KEYTAB_NAME_LEN 1100
      40             : #endif
      41             : 
      42         126 : static krb5_error_code ads_keytab_open(krb5_context context,
      43             :                                        krb5_keytab *keytab)
      44             : {
      45         126 :         char keytab_str[MAX_KEYTAB_NAME_LEN] = {0};
      46         126 :         const char *keytab_name = NULL;
      47         126 :         krb5_error_code ret = 0;
      48             : 
      49         126 :         switch (lp_kerberos_method()) {
      50           0 :         case KERBEROS_VERIFY_SYSTEM_KEYTAB:
      51             :         case KERBEROS_VERIFY_SECRETS_AND_KEYTAB:
      52           0 :                 ret = krb5_kt_default_name(context,
      53             :                                            keytab_str,
      54             :                                            sizeof(keytab_str) - 2);
      55           0 :                 if (ret != 0) {
      56           0 :                         DBG_WARNING("Failed to get default keytab name\n");
      57           0 :                         goto out;
      58             :                 }
      59           0 :                 keytab_name = keytab_str;
      60           0 :                 break;
      61         126 :         case KERBEROS_VERIFY_DEDICATED_KEYTAB:
      62         126 :                 keytab_name = lp_dedicated_keytab_file();
      63         126 :                 break;
      64           0 :         default:
      65           0 :                 DBG_ERR("Invalid kerberos method set (%d)\n",
      66             :                         lp_kerberos_method());
      67           0 :                 ret = KRB5_KT_BADNAME;
      68           0 :                 goto out;
      69             :         }
      70             : 
      71         126 :         if (keytab_name == NULL || keytab_name[0] == '\0') {
      72           0 :                 DBG_ERR("Invalid keytab name\n");
      73           0 :                 ret = KRB5_KT_BADNAME;
      74           0 :                 goto out;
      75             :         }
      76             : 
      77         126 :         ret = smb_krb5_kt_open(context, keytab_name, true, keytab);
      78         126 :         if (ret != 0) {
      79           0 :                 DBG_WARNING("smb_krb5_kt_open failed (%s)\n",
      80             :                             error_message(ret));
      81           0 :                 goto out;
      82             :         }
      83             : 
      84         126 : out:
      85         126 :         return ret;
      86             : }
      87             : 
      88           2 : static bool fill_default_spns(TALLOC_CTX *ctx, const char *machine_name,
      89             :                                           const char *my_fqdn, const char *spn,
      90             :                                           const char ***spns)
      91             : {
      92           0 :         char *psp1, *psp2;
      93             : 
      94           2 :         if (*spns == NULL) {
      95           2 :                 *spns = talloc_zero_array(ctx, const char*, 3);
      96           2 :                 if (*spns == NULL) {
      97           0 :                         return false;
      98             :                 }
      99             :         }
     100             : 
     101           2 :         psp1 = talloc_asprintf(ctx,
     102             :                                "%s/%s",
     103             :                                spn,
     104             :                                machine_name);
     105           2 :         if (psp1 == NULL) {
     106           0 :                 return false;
     107             :         }
     108             : 
     109           2 :         if (!strlower_m(&psp1[strlen(spn) + 1])) {
     110           0 :                 return false;
     111             :         }
     112           2 :         (*spns)[0] = psp1;
     113             : 
     114           2 :         psp2 = talloc_asprintf(ctx,
     115             :                                "%s/%s",
     116             :                                spn,
     117             :                                my_fqdn);
     118           2 :         if (psp2 == NULL) {
     119           0 :                 return false;
     120             :         }
     121             : 
     122           2 :         if (!strlower_m(&psp2[strlen(spn) + 1])) {
     123           0 :                 return false;
     124             :         }
     125             : 
     126           2 :         (*spns)[1] = psp2;
     127             : 
     128           2 :         return true;
     129             : }
     130             : 
     131           2 : static bool ads_set_machine_account_spns(TALLOC_CTX *ctx,
     132             :                                          ADS_STRUCT *ads,
     133             :                                          const char *service_or_spn,
     134             :                                          const char *my_fqdn)
     135             : {
     136           2 :         const char **spn_names = NULL;
     137           0 :         ADS_STATUS aderr;
     138           2 :         struct spn_struct* spn_struct = NULL;
     139           2 :         char *tmp = NULL;
     140             : 
     141             :         /* SPN should have '/' */
     142           2 :         tmp = strchr_m(service_or_spn, '/');
     143           2 :         if (tmp != NULL) {
     144           0 :                 spn_struct = parse_spn(ctx, service_or_spn);
     145           0 :                 if (spn_struct == NULL) {
     146           0 :                         return false;
     147             :                 }
     148             :         }
     149             : 
     150           2 :         DBG_INFO("Attempting to add/update '%s'\n", service_or_spn);
     151             : 
     152           2 :         if (spn_struct != NULL) {
     153           0 :                 spn_names = talloc_zero_array(ctx, const char*, 2);
     154           0 :                 spn_names[0] = service_or_spn;
     155             :         } else {
     156           0 :                 bool ok;
     157             : 
     158           2 :                 ok = fill_default_spns(ctx,
     159             :                                        lp_netbios_name(),
     160             :                                        my_fqdn,
     161             :                                        service_or_spn,
     162             :                                        &spn_names);
     163           2 :                 if (!ok) {
     164           0 :                         return false;
     165             :                 }
     166             :         }
     167           2 :         aderr = ads_add_service_principal_names(ads,
     168             :                                                 lp_netbios_name(),
     169             :                                                 spn_names);
     170           2 :         if (!ADS_ERR_OK(aderr)) {
     171           0 :                 DBG_WARNING("Failed to add service principal name.\n");
     172           0 :                 return false;
     173             :         }
     174             : 
     175           2 :         return true;
     176             : }
     177             : 
     178             : /*
     179             :  * Create kerberos principal(s) from SPN or service name.
     180             :  */
     181          96 : static bool service_or_spn_to_kerberos_princ(TALLOC_CTX *ctx,
     182             :                                              const char *service_or_spn,
     183             :                                              const char *my_fqdn,
     184             :                                              char **p_princ_s,
     185             :                                              char **p_short_princ_s)
     186             : {
     187          96 :         char *princ_s = NULL;
     188          96 :         char *short_princ_s = NULL;
     189          96 :         const char *service = service_or_spn;
     190          96 :         const char *host = my_fqdn;
     191          96 :         struct spn_struct* spn_struct = NULL;
     192          96 :         char *tmp = NULL;
     193          96 :         bool ok = true;
     194             : 
     195             :         /* SPN should have '/' */
     196          96 :         tmp = strchr_m(service_or_spn, '/');
     197          96 :         if (tmp != NULL) {
     198          12 :                 spn_struct = parse_spn(ctx, service_or_spn);
     199          12 :                 if (spn_struct == NULL) {
     200           4 :                         ok = false;
     201           4 :                         goto out;
     202             :                 }
     203             :         }
     204          92 :         if (spn_struct != NULL) {
     205           8 :                 service = spn_struct->serviceclass;
     206           8 :                 host = spn_struct->host;
     207             :         }
     208          92 :         princ_s = talloc_asprintf(ctx, "%s/%s@%s",
     209             :                                   service,
     210             :                                   host, lp_realm());
     211          92 :         if (princ_s == NULL) {
     212           0 :                 ok = false;
     213           0 :                 goto out;
     214             :         }
     215             : 
     216          92 :         if (spn_struct == NULL) {
     217          84 :                 short_princ_s = talloc_asprintf(ctx, "%s/%s@%s",
     218             :                                         service, lp_netbios_name(),
     219             :                                         lp_realm());
     220          84 :                 if (short_princ_s == NULL) {
     221           0 :                         ok = false;
     222           0 :                         goto out;
     223             :                 }
     224             :         }
     225          92 :         *p_princ_s = princ_s;
     226          92 :         *p_short_princ_s = short_princ_s;
     227          96 : out:
     228          96 :         return ok;
     229             : }
     230             : 
     231         108 : static int add_kt_entry_etypes(krb5_context context, TALLOC_CTX *tmpctx,
     232             :                                ADS_STRUCT *ads, const char *salt_princ_s,
     233             :                                krb5_keytab keytab, krb5_kvno kvno,
     234             :                                const char *srvPrinc, const char *my_fqdn,
     235             :                                krb5_data *password, bool update_ads)
     236             : {
     237         108 :         krb5_error_code ret = 0;
     238         108 :         char *princ_s = NULL;
     239         108 :         char *short_princ_s = NULL;
     240         108 :         krb5_enctype enctypes[4] = {
     241             :                 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
     242             :                 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
     243             :                 ENCTYPE_ARCFOUR_HMAC,
     244             :                 0
     245             :         };
     246           0 :         size_t i;
     247             : 
     248             :         /* Construct our principal */
     249         108 :         if (strchr_m(srvPrinc, '@')) {
     250             :                 /* It's a fully-named principal. */
     251           4 :                 princ_s = talloc_asprintf(tmpctx, "%s", srvPrinc);
     252           4 :                 if (!princ_s) {
     253           0 :                         ret = -1;
     254           0 :                         goto out;
     255             :                 }
     256         104 :         } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
     257             :                 /* It's the machine account, as used by smbclient clients. */
     258          16 :                 princ_s = talloc_asprintf(tmpctx, "%s@%s",
     259             :                                           srvPrinc, lp_realm());
     260          16 :                 if (!princ_s) {
     261           0 :                         ret = -1;
     262           0 :                         goto out;
     263             :                 }
     264             :         } else {
     265             :                 /* It's a normal service principal.  Add the SPN now so that we
     266             :                  * can obtain credentials for it and double-check the salt value
     267             :                  * used to generate the service's keys. */
     268             : 
     269          88 :                 if (!service_or_spn_to_kerberos_princ(tmpctx,
     270             :                                                       srvPrinc,
     271             :                                                       my_fqdn,
     272             :                                                       &princ_s,
     273             :                                                       &short_princ_s)) {
     274           4 :                         ret = -1;
     275           4 :                         goto out;
     276             :                 }
     277             : 
     278             :                 /* According to http://support.microsoft.com/kb/326985/en-us,
     279             :                    certain principal names are automatically mapped to the
     280             :                    host/... principal in the AD account.
     281             :                    So only create these in the keytab, not in AD.  --jerry */
     282             : 
     283          84 :                 if (update_ads && !strequal(srvPrinc, "cifs") &&
     284           2 :                     !strequal(srvPrinc, "host")) {
     285           2 :                         if (!ads_set_machine_account_spns(tmpctx,
     286             :                                                           ads,
     287             :                                                           srvPrinc,
     288             :                                                           my_fqdn)) {
     289           0 :                                 ret = -1;
     290           0 :                                 goto out;
     291             :                         }
     292             :                 }
     293             :         }
     294             : 
     295         416 :         for (i = 0; enctypes[i]; i++) {
     296             : 
     297             :                 /* add the fqdn principal to the keytab */
     298         312 :                 ret = smb_krb5_kt_add_entry(context,
     299             :                                             keytab,
     300             :                                             kvno,
     301             :                                             princ_s,
     302             :                                             salt_princ_s,
     303             :                                             enctypes[i],
     304             :                                             password,
     305             :                                             false); /* no_salt */
     306         312 :                 if (ret) {
     307           0 :                         DBG_WARNING("Failed to add entry to keytab\n");
     308           0 :                         goto out;
     309             :                 }
     310             : 
     311             :                 /* add the short principal name if we have one */
     312         312 :                 if (short_princ_s) {
     313         240 :                         ret = smb_krb5_kt_add_entry(context,
     314             :                                                     keytab,
     315             :                                                     kvno,
     316             :                                                     short_princ_s,
     317             :                                                     salt_princ_s,
     318             :                                                     enctypes[i],
     319             :                                                     password,
     320             :                                                     false); /* no_salt */
     321         240 :                         if (ret) {
     322           0 :                                 DBG_WARNING("Failed to add short entry to keytab\n");
     323           0 :                                 goto out;
     324             :                         }
     325             :                 }
     326             :         }
     327         104 : out:
     328         108 :         return ret;
     329             : }
     330             : 
     331             : /**********************************************************************
     332             :  Adds a single service principal, i.e. 'host' to the system keytab
     333             : ***********************************************************************/
     334             : 
     335          66 : int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc, bool update_ads)
     336             : {
     337          66 :         krb5_error_code ret = 0;
     338          66 :         krb5_context context = NULL;
     339          66 :         krb5_keytab keytab = NULL;
     340           0 :         krb5_data password;
     341           0 :         krb5_kvno kvno;
     342          66 :         char *salt_princ_s = NULL;
     343          66 :         char *password_s = NULL;
     344           0 :         char *my_fqdn;
     345          66 :         TALLOC_CTX *tmpctx = NULL;
     346          66 :         char **hostnames_array = NULL;
     347          66 :         size_t num_hostnames = 0;
     348             : 
     349          66 :         ret = smb_krb5_init_context_common(&context);
     350          66 :         if (ret) {
     351           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     352             :                         error_message(ret));
     353           0 :                 return -1;
     354             :         }
     355             : 
     356          66 :         ret = ads_keytab_open(context, &keytab);
     357          66 :         if (ret != 0) {
     358           0 :                 goto out;
     359             :         }
     360             : 
     361             :         /* retrieve the password */
     362          66 :         if (!secrets_init()) {
     363           0 :                 DBG_WARNING("secrets_init failed\n");
     364           0 :                 ret = -1;
     365           0 :                 goto out;
     366             :         }
     367          66 :         password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
     368          66 :         if (!password_s) {
     369           0 :                 DBG_WARNING("failed to fetch machine password\n");
     370           0 :                 ret = -1;
     371           0 :                 goto out;
     372             :         }
     373          66 :         ZERO_STRUCT(password);
     374          66 :         password.data = password_s;
     375          66 :         password.length = strlen(password_s);
     376             : 
     377             :         /* we need the dNSHostName value here */
     378          66 :         tmpctx = talloc_init(__location__);
     379          66 :         if (!tmpctx) {
     380           0 :                 DBG_ERR("talloc_init() failed!\n");
     381           0 :                 ret = -1;
     382           0 :                 goto out;
     383             :         }
     384             : 
     385          66 :         my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
     386          66 :         if (!my_fqdn) {
     387           0 :                 DBG_ERR("unable to determine machine account's dns name in "
     388             :                         "AD!\n");
     389           0 :                 ret = -1;
     390           0 :                 goto out;
     391             :         }
     392             : 
     393             :         /* make sure we have a single instance of the computer account */
     394          66 :         if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
     395           0 :                 DBG_ERR("unable to determine machine account's short name in "
     396             :                         "AD!\n");
     397           0 :                 ret = -1;
     398           0 :                 goto out;
     399             :         }
     400             : 
     401          66 :         kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
     402          66 :         if (kvno == -1) {
     403             :                 /* -1 indicates failure, everything else is OK */
     404           0 :                 DBG_WARNING("ads_get_machine_kvno failed to determine the "
     405             :                             "system's kvno.\n");
     406           0 :                 ret = -1;
     407           0 :                 goto out;
     408             :         }
     409             : 
     410          66 :         salt_princ_s = kerberos_secrets_fetch_salt_princ();
     411          66 :         if (salt_princ_s == NULL) {
     412           0 :                 DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
     413           0 :                 ret = -1;
     414           0 :                 goto out;
     415             :         }
     416             : 
     417          66 :         ret = add_kt_entry_etypes(context, tmpctx, ads, salt_princ_s, keytab,
     418             :                                   kvno, srvPrinc, my_fqdn, &password,
     419             :                                   update_ads);
     420          66 :         if (ret != 0) {
     421           4 :                 goto out;
     422             :         }
     423             : 
     424          62 :         if (ADS_ERR_OK(ads_get_additional_dns_hostnames(tmpctx, ads,
     425             :                                                         lp_netbios_name(),
     426             :                                                         &hostnames_array,
     427             :                                                         &num_hostnames))) {
     428             :                 size_t i;
     429             : 
     430          56 :                 for (i = 0; i < num_hostnames; i++) {
     431             : 
     432          42 :                         ret = add_kt_entry_etypes(context, tmpctx, ads,
     433             :                                                   salt_princ_s, keytab,
     434             :                                                   kvno, srvPrinc,
     435          42 :                                                   hostnames_array[i],
     436             :                                                   &password, update_ads);
     437          42 :                         if (ret != 0) {
     438           0 :                                 goto out;
     439             :                         }
     440             :                 }
     441             :         }
     442             : 
     443          62 : out:
     444          66 :         SAFE_FREE(salt_princ_s);
     445          66 :         TALLOC_FREE(tmpctx);
     446             : 
     447          66 :         if (keytab) {
     448          66 :                 krb5_kt_close(context, keytab);
     449             :         }
     450          66 :         if (context) {
     451          66 :                 krb5_free_context(context);
     452             :         }
     453          66 :         return (int)ret;
     454             : }
     455             : 
     456             : /**********************************************************************
     457             :  Delete a single service principal, i.e. 'host' from the system keytab
     458             : ***********************************************************************/
     459             : 
     460          12 : int ads_keytab_delete_entry(ADS_STRUCT *ads, const char *srvPrinc)
     461             : {
     462          12 :         TALLOC_CTX *frame = talloc_stackframe();
     463          12 :         krb5_error_code ret = 0;
     464          12 :         krb5_context context = NULL;
     465          12 :         krb5_keytab keytab = NULL;
     466          12 :         char *princ_s = NULL;
     467          12 :         krb5_principal princ = NULL;
     468          12 :         char *short_princ_s = NULL;
     469          12 :         krb5_principal short_princ = NULL;
     470           0 :         bool ok;
     471             : 
     472          12 :         ret = smb_krb5_init_context_common(&context);
     473          12 :         if (ret) {
     474           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     475             :                         error_message(ret));
     476           0 :                 goto out;
     477             :         }
     478             : 
     479          12 :         ret = ads_keytab_open(context, &keytab);
     480          12 :         if (ret != 0) {
     481           0 :                 goto out;
     482             :         }
     483             : 
     484             :         /* Construct our principal */
     485          12 :         if (strchr_m(srvPrinc, '@')) {
     486             :                 /* It's a fully-named principal. */
     487           2 :                 princ_s = talloc_asprintf(frame, "%s", srvPrinc);
     488           2 :                 if (!princ_s) {
     489           0 :                         ret = -1;
     490           0 :                         goto out;
     491             :                 }
     492          10 :         } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
     493             :                 /* It's the machine account, as used by smbclient clients. */
     494           2 :                 princ_s = talloc_asprintf(frame, "%s@%s",
     495             :                                           srvPrinc, lp_realm());
     496           2 :                 if (!princ_s) {
     497           0 :                         ret = -1;
     498           0 :                         goto out;
     499             :                 }
     500             :         } else {
     501             :                 /*
     502             :                  * It's a normal service principal.
     503             :                  */
     504           8 :                 char *my_fqdn = NULL;
     505           8 :                 char *tmp = NULL;
     506             : 
     507             :                 /*
     508             :                  * SPN should have '/' otherwise we
     509             :                  * need to fallback and find our dnshostname
     510             :                  */
     511           8 :                 tmp = strchr_m(srvPrinc, '/');
     512           8 :                 if (tmp == NULL) {
     513           4 :                         my_fqdn = ads_get_dnshostname(ads, frame, lp_netbios_name());
     514           4 :                         if (!my_fqdn) {
     515           0 :                                 DBG_ERR("unable to determine machine account's dns name in "
     516             :                                         "AD!\n");
     517           0 :                                 ret = -1;
     518           0 :                                 goto out;
     519             :                         }
     520             :                 }
     521             : 
     522           8 :                 ok = service_or_spn_to_kerberos_princ(frame,
     523             :                                                       srvPrinc,
     524             :                                                       my_fqdn,
     525             :                                                       &princ_s,
     526             :                                                       &short_princ_s);
     527           8 :                 if (!ok) {
     528           0 :                         ret = -1;
     529           0 :                         goto out;
     530             :                 }
     531             :         }
     532             : 
     533          12 :         ret = smb_krb5_parse_name(context, princ_s, &princ);
     534          12 :         if (ret) {
     535           0 :                 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
     536             :                           "failed (%s)\n", princ_s, error_message(ret)));
     537           0 :                 goto out;
     538             :         }
     539             : 
     540          12 :         if (short_princ_s != NULL) {
     541           4 :                 ret = smb_krb5_parse_name(context, short_princ_s, &short_princ);
     542           4 :                 if (ret) {
     543           0 :                         DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
     544             :                                   "failed (%s)\n", short_princ_s, error_message(ret)));
     545           0 :                         goto out;
     546             :                 }
     547             :         }
     548             : 
     549             :         /* Seek and delete old keytab entries */
     550          12 :         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
     551             :                                                       keytab,
     552             :                                                       false, /* keep_old_kvno */
     553             :                                                       -1,
     554             :                                                       false, /* enctype_only */
     555             :                                                       ENCTYPE_NULL,
     556             :                                                       princ_s,
     557             :                                                       princ,
     558             :                                                       false); /* flush */
     559          12 :         if (ret) {
     560           0 :                 goto out;
     561             :         }
     562             : 
     563          12 :         if (short_princ_s == NULL) {
     564           8 :                 goto out;
     565             :         }
     566             : 
     567             :         /* Seek and delete old keytab entries */
     568           4 :         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
     569             :                                                       keytab,
     570             :                                                       false, /* keep_old_kvno */
     571             :                                                       -1,
     572             :                                                       false, /* enctype_only */
     573             :                                                       ENCTYPE_NULL,
     574             :                                                       short_princ_s,
     575             :                                                       short_princ,
     576             :                                                       false); /* flush */
     577           4 :         if (ret) {
     578           0 :                 goto out;
     579             :         }
     580             : 
     581           4 : out:
     582          12 :         if (princ) {
     583          12 :                 krb5_free_principal(context, princ);
     584             :         }
     585          12 :         if (short_princ) {
     586           4 :                 krb5_free_principal(context, short_princ);
     587             :         }
     588          12 :         if (keytab) {
     589          12 :                 krb5_kt_close(context, keytab);
     590             :         }
     591          12 :         if (context) {
     592          12 :                 krb5_free_context(context);
     593             :         }
     594          12 :         TALLOC_FREE(frame);
     595          12 :         return ret;
     596             : }
     597             : 
     598             : /**********************************************************************
     599             :  Flushes all entries from the system keytab.
     600             : ***********************************************************************/
     601             : 
     602           0 : int ads_keytab_flush(ADS_STRUCT *ads)
     603             : {
     604           0 :         krb5_error_code ret = 0;
     605           0 :         krb5_context context = NULL;
     606           0 :         krb5_keytab keytab = NULL;
     607           0 :         ADS_STATUS aderr;
     608             : 
     609           0 :         ret = smb_krb5_init_context_common(&context);
     610           0 :         if (ret) {
     611           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     612             :                         error_message(ret));
     613           0 :                 return ret;
     614             :         }
     615             : 
     616           0 :         ret = ads_keytab_open(context, &keytab);
     617           0 :         if (ret != 0) {
     618           0 :                 goto out;
     619             :         }
     620             : 
     621             :         /* Seek and delete all old keytab entries */
     622           0 :         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
     623             :                                                       keytab,
     624             :                                                       false, /* keep_old_kvno */
     625             :                                                       -1,
     626             :                                                       false, /* enctype_only */
     627             :                                                       ENCTYPE_NULL,
     628             :                                                       NULL,
     629             :                                                       NULL,
     630             :                                                       true); /* flush */
     631           0 :         if (ret) {
     632           0 :                 goto out;
     633             :         }
     634             : 
     635           0 :         aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
     636           0 :         if (!ADS_ERR_OK(aderr)) {
     637           0 :                 DEBUG(1, (__location__ ": Error while clearing service "
     638             :                           "principal listings in LDAP.\n"));
     639           0 :                 ret = -1;
     640           0 :                 goto out;
     641             :         }
     642             : 
     643           0 : out:
     644           0 :         if (keytab) {
     645           0 :                 krb5_kt_close(context, keytab);
     646             :         }
     647           0 :         if (context) {
     648           0 :                 krb5_free_context(context);
     649             :         }
     650           0 :         return ret;
     651             : }
     652             : 
     653             : /**********************************************************************
     654             :  Adds all the required service principals to the system keytab.
     655             : ***********************************************************************/
     656             : 
     657           8 : int ads_keytab_create_default(ADS_STRUCT *ads)
     658             : {
     659           8 :         krb5_error_code ret = 0;
     660           8 :         krb5_context context = NULL;
     661           8 :         krb5_keytab keytab = NULL;
     662           8 :         krb5_kt_cursor cursor = {0};
     663           8 :         krb5_keytab_entry kt_entry = {0};
     664           0 :         krb5_kvno kvno;
     665           8 :         size_t found = 0;
     666           0 :         char *sam_account_name, *upn;
     667           8 :         char **oldEntries = NULL, *princ_s[26];
     668           0 :         TALLOC_CTX *frame;
     669           0 :         char *machine_name;
     670           0 :         char **spn_array;
     671           0 :         size_t num_spns;
     672           0 :         size_t i;
     673           8 :         bool ok = false;
     674           0 :         ADS_STATUS status;
     675             : 
     676           8 :         ZERO_STRUCT(kt_entry);
     677           8 :         ZERO_STRUCT(cursor);
     678             : 
     679           8 :         frame = talloc_stackframe();
     680           8 :         if (frame == NULL) {
     681           0 :                 ret = -1;
     682           0 :                 goto done;
     683             :         }
     684             : 
     685           8 :         status = ads_get_service_principal_names(frame,
     686             :                                                  ads,
     687             :                                                  lp_netbios_name(),
     688             :                                                  &spn_array,
     689             :                                                  &num_spns);
     690           8 :         if (!ADS_ERR_OK(status)) {
     691           0 :                 ret = -1;
     692           0 :                 goto done;
     693             :         }
     694             : 
     695          48 :         for (i = 0; i < num_spns; i++) {
     696           0 :                 char *srv_princ;
     697           0 :                 char *p;
     698             : 
     699          40 :                 srv_princ = strlower_talloc(frame, spn_array[i]);
     700          40 :                 if (srv_princ == NULL) {
     701           0 :                         ret = -1;
     702           0 :                         goto done;
     703             :                 }
     704             : 
     705          40 :                 p = strchr_m(srv_princ, '/');
     706          40 :                 if (p == NULL) {
     707           0 :                         continue;
     708             :                 }
     709          40 :                 p[0] = '\0';
     710             : 
     711             :                 /* Add the SPNs found on the DC */
     712          40 :                 ret = ads_keytab_add_entry(ads, srv_princ, false);
     713          40 :                 if (ret != 0) {
     714           0 :                         DEBUG(1, ("ads_keytab_add_entry failed while "
     715             :                                   "adding '%s' principal.\n",
     716             :                                   spn_array[i]));
     717           0 :                         goto done;
     718             :                 }
     719             :         }
     720             : 
     721             : #if 0   /* don't create the CIFS/... keytab entries since no one except smbd
     722             :            really needs them and we will fall back to verifying against
     723             :            secrets.tdb */
     724             : 
     725             :         ret = ads_keytab_add_entry(ads, "cifs", false));
     726             :         if (ret != 0 ) {
     727             :                 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
     728             :                           "adding 'cifs'.\n"));
     729             :                 return ret;
     730             :         }
     731             : #endif
     732             : 
     733           8 :         memset(princ_s, '\0', sizeof(princ_s));
     734             : 
     735           8 :         ret = smb_krb5_init_context_common(&context);
     736           8 :         if (ret) {
     737           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     738             :                         error_message(ret));
     739           0 :                 goto done;
     740             :         }
     741             : 
     742           8 :         machine_name = talloc_strdup(frame, lp_netbios_name());
     743           8 :         if (!machine_name) {
     744           0 :                 ret = -1;
     745           0 :                 goto done;
     746             :         }
     747             : 
     748             :         /* now add the userPrincipalName and sAMAccountName entries */
     749           8 :         ok = ads_has_samaccountname(ads, frame, machine_name);
     750           8 :         if (!ok) {
     751           0 :                 DEBUG(0, (__location__ ": unable to determine machine "
     752             :                           "account's name in AD!\n"));
     753           0 :                 ret = -1;
     754           0 :                 goto done;
     755             :         }
     756             : 
     757             :         /*
     758             :          * append '$' to netbios name so 'ads_keytab_add_entry' recognises
     759             :          * it as a machine account rather than a service or Windows SPN.
     760             :          */
     761           8 :         sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
     762           8 :         if (sam_account_name == NULL) {
     763           0 :                 ret = -1;
     764           0 :                 goto done;
     765             :         }
     766             :         /* upper case the sAMAccountName to make it easier for apps to
     767             :            know what case to use in the keytab file */
     768           8 :         if (!strupper_m(sam_account_name)) {
     769           0 :                 ret = -1;
     770           0 :                 goto done;
     771             :         }
     772             : 
     773           8 :         ret = ads_keytab_add_entry(ads, sam_account_name, false);
     774           8 :         if (ret != 0) {
     775           0 :                 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
     776             :                           "while adding sAMAccountName (%s)\n",
     777             :                           sam_account_name));
     778           0 :                 goto done;
     779             :         }
     780             : 
     781             :         /* remember that not every machine account will have a upn */
     782           8 :         upn = ads_get_upn(ads, frame, machine_name);
     783           8 :         if (upn) {
     784           2 :                 ret = ads_keytab_add_entry(ads, upn, false);
     785           2 :                 if (ret != 0) {
     786           0 :                         DEBUG(1, (__location__ ": ads_keytab_add_entry() "
     787             :                                   "failed while adding UPN (%s)\n", upn));
     788           0 :                         goto done;
     789             :                 }
     790             :         }
     791             : 
     792             :         /* Now loop through the keytab and update any other existing entries */
     793           8 :         kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
     794           8 :         if (kvno == (krb5_kvno)-1) {
     795           0 :                 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
     796             :                           "determine the system's kvno.\n"));
     797           0 :                 goto done;
     798             :         }
     799             : 
     800           8 :         DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
     801             :                   "and update.\n"));
     802             : 
     803           8 :         ret = ads_keytab_open(context, &keytab);
     804           8 :         if (ret != 0) {
     805           0 :                 goto done;
     806             :         }
     807             : 
     808           8 :         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
     809           8 :         if (ret != KRB5_KT_END && ret != ENOENT ) {
     810         212 :                 while ((ret = krb5_kt_next_entry(context, keytab,
     811         212 :                                                  &kt_entry, &cursor)) == 0) {
     812         204 :                         smb_krb5_kt_free_entry(context, &kt_entry);
     813         204 :                         ZERO_STRUCT(kt_entry);
     814         204 :                         found++;
     815             :                 }
     816             :         }
     817           8 :         krb5_kt_end_seq_get(context, keytab, &cursor);
     818           8 :         ZERO_STRUCT(cursor);
     819             : 
     820             :         /*
     821             :          * Hmmm. There is no "rewind" function for the keytab. This means we
     822             :          * have a race condition where someone else could add entries after
     823             :          * we've counted them. Re-open asap to minimise the race. JRA.
     824             :          */
     825           8 :         DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
     826           8 :         if (!found) {
     827           0 :                 goto done;
     828             :         }
     829             : 
     830           8 :         oldEntries = talloc_zero_array(frame, char *, found + 1);
     831           8 :         if (!oldEntries) {
     832           0 :                 DEBUG(1, (__location__ ": Failed to allocate space to store "
     833             :                           "the old keytab entries (talloc failed?).\n"));
     834           0 :                 ret = -1;
     835           0 :                 goto done;
     836             :         }
     837             : 
     838           8 :         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
     839           8 :         if (ret == KRB5_KT_END || ret == ENOENT) {
     840           0 :                 krb5_kt_end_seq_get(context, keytab, &cursor);
     841           0 :                 ZERO_STRUCT(cursor);
     842           0 :                 goto done;
     843             :         }
     844             : 
     845         212 :         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
     846         204 :                 if (kt_entry.vno != kvno) {
     847           0 :                         char *ktprinc = NULL;
     848           0 :                         char *p;
     849             : 
     850             :                         /* This returns a malloc'ed string in ktprinc. */
     851           0 :                         ret = smb_krb5_unparse_name(oldEntries, context,
     852           0 :                                                     kt_entry.principal,
     853             :                                                     &ktprinc);
     854           0 :                         if (ret) {
     855           0 :                                 DEBUG(1, (__location__
     856             :                                          ": smb_krb5_unparse_name failed "
     857             :                                          "(%s)\n", error_message(ret)));
     858           0 :                                 goto done;
     859             :                         }
     860             :                         /*
     861             :                          * From looking at the krb5 source they don't seem to
     862             :                          * take locale or mb strings into account.
     863             :                          * Maybe this is because they assume utf8 ?
     864             :                          * In this case we may need to convert from utf8 to
     865             :                          * mb charset here ? JRA.
     866             :                          */
     867           0 :                         p = strchr_m(ktprinc, '@');
     868           0 :                         if (p) {
     869           0 :                                 *p = '\0';
     870             :                         }
     871             : 
     872           0 :                         p = strchr_m(ktprinc, '/');
     873           0 :                         if (p) {
     874           0 :                                 *p = '\0';
     875             :                         }
     876           0 :                         for (i = 0; i < found; i++) {
     877           0 :                                 if (!oldEntries[i]) {
     878           0 :                                         oldEntries[i] = ktprinc;
     879           0 :                                         break;
     880             :                                 }
     881           0 :                                 if (!strcmp(oldEntries[i], ktprinc)) {
     882           0 :                                         TALLOC_FREE(ktprinc);
     883           0 :                                         break;
     884             :                                 }
     885             :                         }
     886           0 :                         if (i == found) {
     887           0 :                                 TALLOC_FREE(ktprinc);
     888             :                         }
     889             :                 }
     890         204 :                 smb_krb5_kt_free_entry(context, &kt_entry);
     891         204 :                 ZERO_STRUCT(kt_entry);
     892             :         }
     893           8 :         krb5_kt_end_seq_get(context, keytab, &cursor);
     894           8 :         ZERO_STRUCT(cursor);
     895             : 
     896           8 :         ret = 0;
     897           8 :         for (i = 0; oldEntries[i]; i++) {
     898           0 :                 ret |= ads_keytab_add_entry(ads, oldEntries[i], false);
     899           0 :                 TALLOC_FREE(oldEntries[i]);
     900             :         }
     901             : 
     902           8 : done:
     903           8 :         TALLOC_FREE(oldEntries);
     904           8 :         TALLOC_FREE(frame);
     905             : 
     906           8 :         if (context) {
     907           8 :                 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
     908           0 :                         smb_krb5_kt_free_entry(context, &kt_entry);
     909             :                 }
     910           8 :                 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
     911           0 :                         krb5_kt_end_seq_get(context, keytab, &cursor);
     912             :                 }
     913           8 :                 if (keytab) {
     914           8 :                         krb5_kt_close(context, keytab);
     915             :                 }
     916           8 :                 krb5_free_context(context);
     917             :         }
     918           8 :         return ret;
     919             : }
     920             : 
     921             : #endif /* HAVE_ADS */
     922             : 
     923             : /**********************************************************************
     924             :  List system keytab.
     925             : ***********************************************************************/
     926             : 
     927          42 : int ads_keytab_list(const char *keytab_name)
     928             : {
     929          42 :         krb5_error_code ret = 0;
     930          42 :         krb5_context context = NULL;
     931          42 :         krb5_keytab keytab = NULL;
     932           0 :         krb5_kt_cursor cursor;
     933           0 :         krb5_keytab_entry kt_entry;
     934             : 
     935          42 :         ZERO_STRUCT(kt_entry);
     936          42 :         ZERO_STRUCT(cursor);
     937             : 
     938          42 :         ret = smb_krb5_init_context_common(&context);
     939          42 :         if (ret) {
     940           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     941             :                         error_message(ret));
     942           0 :                 return ret;
     943             :         }
     944             : 
     945          42 :         if (keytab_name == NULL) {
     946             : #ifdef HAVE_ADS
     947          40 :                 ret = ads_keytab_open(context, &keytab);
     948             : #else
     949           0 :                 ret = ENOENT;
     950             : #endif
     951             :         } else {
     952           2 :                 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
     953             :         }
     954          42 :         if (ret) {
     955           0 :                 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
     956             :                           error_message(ret)));
     957           0 :                 goto out;
     958             :         }
     959             : 
     960          42 :         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
     961          42 :         if (ret) {
     962           0 :                 ZERO_STRUCT(cursor);
     963           0 :                 goto out;
     964             :         }
     965             : 
     966          42 :         printf("Vno  Type                                        Principal\n");
     967             : 
     968         924 :         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
     969             : 
     970         882 :                 char *princ_s = NULL;
     971         882 :                 char *etype_s = NULL;
     972         882 :                 krb5_enctype enctype = 0;
     973             : 
     974         882 :                 ret = smb_krb5_unparse_name(talloc_tos(), context,
     975         882 :                                             kt_entry.principal, &princ_s);
     976         882 :                 if (ret) {
     977           0 :                         goto out;
     978             :                 }
     979             : 
     980         882 :                 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
     981             : 
     982         882 :                 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
     983         882 :                 if (ret &&
     984           0 :                     (asprintf(&etype_s, "UNKNOWN: %d", enctype) == -1)) {
     985           0 :                         TALLOC_FREE(princ_s);
     986           0 :                         goto out;
     987             :                 }
     988             : 
     989         882 :                 printf("%3d  %-43s %s\n", kt_entry.vno, etype_s, princ_s);
     990             : 
     991         882 :                 TALLOC_FREE(princ_s);
     992         882 :                 SAFE_FREE(etype_s);
     993             : 
     994         882 :                 ret = smb_krb5_kt_free_entry(context, &kt_entry);
     995         882 :                 if (ret) {
     996           0 :                         goto out;
     997             :                 }
     998             :         }
     999             : 
    1000          42 :         ret = krb5_kt_end_seq_get(context, keytab, &cursor);
    1001          42 :         if (ret) {
    1002           0 :                 goto out;
    1003             :         }
    1004             : 
    1005             :         /* Ensure we don't double free. */
    1006          42 :         ZERO_STRUCT(kt_entry);
    1007          42 :         ZERO_STRUCT(cursor);
    1008          42 : out:
    1009             : 
    1010          42 :         if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
    1011           0 :                 smb_krb5_kt_free_entry(context, &kt_entry);
    1012             :         }
    1013          42 :         if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
    1014           0 :                 krb5_kt_end_seq_get(context, keytab, &cursor);
    1015             :         }
    1016             : 
    1017          42 :         if (keytab) {
    1018          42 :                 krb5_kt_close(context, keytab);
    1019             :         }
    1020          42 :         if (context) {
    1021          42 :                 krb5_free_context(context);
    1022             :         }
    1023          42 :         return ret;
    1024             : }
    1025             : 
    1026             : #endif /* HAVE_KRB5 */

Generated by: LCOV version 1.14