LCOV - code coverage report
Current view: top level - source3/libads - kerberos_keytab.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 302 509 59.3 %
Date: 2024-05-31 13:13:24 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_password(context,
     299             :                                                keytab,
     300             :                                                kvno,
     301             :                                                princ_s,
     302             :                                                salt_princ_s,
     303             :                                                enctypes[i],
     304             :                                                password);
     305         312 :                 if (ret) {
     306           0 :                         DBG_WARNING("Failed to add entry to keytab\n");
     307           0 :                         goto out;
     308             :                 }
     309             : 
     310             :                 /* add the short principal name if we have one */
     311         312 :                 if (short_princ_s) {
     312         240 :                         ret = smb_krb5_kt_add_password(context,
     313             :                                                        keytab,
     314             :                                                        kvno,
     315             :                                                        short_princ_s,
     316             :                                                        salt_princ_s,
     317             :                                                        enctypes[i],
     318             :                                                        password);
     319         240 :                         if (ret) {
     320           0 :                                 DBG_WARNING("Failed to add short entry to keytab\n");
     321           0 :                                 goto out;
     322             :                         }
     323             :                 }
     324             :         }
     325         104 : out:
     326         108 :         return ret;
     327             : }
     328             : 
     329             : /**********************************************************************
     330             :  Adds a single service principal, i.e. 'host' to the system keytab
     331             : ***********************************************************************/
     332             : 
     333          66 : int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc, bool update_ads)
     334             : {
     335          66 :         krb5_error_code ret = 0;
     336          66 :         krb5_context context = NULL;
     337          66 :         krb5_keytab keytab = NULL;
     338           0 :         krb5_data password;
     339           0 :         krb5_kvno kvno;
     340          66 :         char *salt_princ_s = NULL;
     341          66 :         char *password_s = NULL;
     342           0 :         char *my_fqdn;
     343          66 :         TALLOC_CTX *tmpctx = NULL;
     344          66 :         char **hostnames_array = NULL;
     345          66 :         size_t num_hostnames = 0;
     346             : 
     347          66 :         ret = smb_krb5_init_context_common(&context);
     348          66 :         if (ret) {
     349           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     350             :                         error_message(ret));
     351           0 :                 return -1;
     352             :         }
     353             : 
     354          66 :         ret = ads_keytab_open(context, &keytab);
     355          66 :         if (ret != 0) {
     356           0 :                 goto out;
     357             :         }
     358             : 
     359             :         /* retrieve the password */
     360          66 :         if (!secrets_init()) {
     361           0 :                 DBG_WARNING("secrets_init failed\n");
     362           0 :                 ret = -1;
     363           0 :                 goto out;
     364             :         }
     365          66 :         password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
     366          66 :         if (!password_s) {
     367           0 :                 DBG_WARNING("failed to fetch machine password\n");
     368           0 :                 ret = -1;
     369           0 :                 goto out;
     370             :         }
     371          66 :         ZERO_STRUCT(password);
     372          66 :         password.data = password_s;
     373          66 :         password.length = strlen(password_s);
     374             : 
     375             :         /* we need the dNSHostName value here */
     376          66 :         tmpctx = talloc_init(__location__);
     377          66 :         if (!tmpctx) {
     378           0 :                 DBG_ERR("talloc_init() failed!\n");
     379           0 :                 ret = -1;
     380           0 :                 goto out;
     381             :         }
     382             : 
     383          66 :         my_fqdn = ads_get_dnshostname(ads, tmpctx, lp_netbios_name());
     384          66 :         if (!my_fqdn) {
     385           0 :                 DBG_ERR("unable to determine machine account's dns name in "
     386             :                         "AD!\n");
     387           0 :                 ret = -1;
     388           0 :                 goto out;
     389             :         }
     390             : 
     391             :         /* make sure we have a single instance of the computer account */
     392          66 :         if (!ads_has_samaccountname(ads, tmpctx, lp_netbios_name())) {
     393           0 :                 DBG_ERR("unable to determine machine account's short name in "
     394             :                         "AD!\n");
     395           0 :                 ret = -1;
     396           0 :                 goto out;
     397             :         }
     398             : 
     399          66 :         kvno = (krb5_kvno)ads_get_machine_kvno(ads, lp_netbios_name());
     400          66 :         if (kvno == -1) {
     401             :                 /* -1 indicates failure, everything else is OK */
     402           0 :                 DBG_WARNING("ads_get_machine_kvno failed to determine the "
     403             :                             "system's kvno.\n");
     404           0 :                 ret = -1;
     405           0 :                 goto out;
     406             :         }
     407             : 
     408          66 :         salt_princ_s = kerberos_secrets_fetch_salt_princ();
     409          66 :         if (salt_princ_s == NULL) {
     410           0 :                 DBG_WARNING("kerberos_secrets_fetch_salt_princ() failed\n");
     411           0 :                 ret = -1;
     412           0 :                 goto out;
     413             :         }
     414             : 
     415          66 :         ret = add_kt_entry_etypes(context, tmpctx, ads, salt_princ_s, keytab,
     416             :                                   kvno, srvPrinc, my_fqdn, &password,
     417             :                                   update_ads);
     418          66 :         if (ret != 0) {
     419           4 :                 goto out;
     420             :         }
     421             : 
     422          62 :         if (ADS_ERR_OK(ads_get_additional_dns_hostnames(tmpctx, ads,
     423             :                                                         lp_netbios_name(),
     424             :                                                         &hostnames_array,
     425             :                                                         &num_hostnames))) {
     426             :                 size_t i;
     427             : 
     428          56 :                 for (i = 0; i < num_hostnames; i++) {
     429             : 
     430          42 :                         ret = add_kt_entry_etypes(context, tmpctx, ads,
     431             :                                                   salt_princ_s, keytab,
     432             :                                                   kvno, srvPrinc,
     433          42 :                                                   hostnames_array[i],
     434             :                                                   &password, update_ads);
     435          42 :                         if (ret != 0) {
     436           0 :                                 goto out;
     437             :                         }
     438             :                 }
     439             :         }
     440             : 
     441          62 : out:
     442          66 :         SAFE_FREE(salt_princ_s);
     443          66 :         TALLOC_FREE(tmpctx);
     444             : 
     445          66 :         if (keytab) {
     446          66 :                 krb5_kt_close(context, keytab);
     447             :         }
     448          66 :         if (context) {
     449          66 :                 krb5_free_context(context);
     450             :         }
     451          66 :         return (int)ret;
     452             : }
     453             : 
     454             : /**********************************************************************
     455             :  Delete a single service principal, i.e. 'host' from the system keytab
     456             : ***********************************************************************/
     457             : 
     458          12 : int ads_keytab_delete_entry(ADS_STRUCT *ads, const char *srvPrinc)
     459             : {
     460          12 :         TALLOC_CTX *frame = talloc_stackframe();
     461          12 :         krb5_error_code ret = 0;
     462          12 :         krb5_context context = NULL;
     463          12 :         krb5_keytab keytab = NULL;
     464          12 :         char *princ_s = NULL;
     465          12 :         krb5_principal princ = NULL;
     466          12 :         char *short_princ_s = NULL;
     467          12 :         krb5_principal short_princ = NULL;
     468           0 :         bool ok;
     469             : 
     470          12 :         ret = smb_krb5_init_context_common(&context);
     471          12 :         if (ret) {
     472           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     473             :                         error_message(ret));
     474           0 :                 goto out;
     475             :         }
     476             : 
     477          12 :         ret = ads_keytab_open(context, &keytab);
     478          12 :         if (ret != 0) {
     479           0 :                 goto out;
     480             :         }
     481             : 
     482             :         /* Construct our principal */
     483          12 :         if (strchr_m(srvPrinc, '@')) {
     484             :                 /* It's a fully-named principal. */
     485           2 :                 princ_s = talloc_asprintf(frame, "%s", srvPrinc);
     486           2 :                 if (!princ_s) {
     487           0 :                         ret = -1;
     488           0 :                         goto out;
     489             :                 }
     490          10 :         } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
     491             :                 /* It's the machine account, as used by smbclient clients. */
     492           2 :                 princ_s = talloc_asprintf(frame, "%s@%s",
     493             :                                           srvPrinc, lp_realm());
     494           2 :                 if (!princ_s) {
     495           0 :                         ret = -1;
     496           0 :                         goto out;
     497             :                 }
     498             :         } else {
     499             :                 /*
     500             :                  * It's a normal service principal.
     501             :                  */
     502           8 :                 char *my_fqdn = NULL;
     503           8 :                 char *tmp = NULL;
     504             : 
     505             :                 /*
     506             :                  * SPN should have '/' otherwise we
     507             :                  * need to fallback and find our dnshostname
     508             :                  */
     509           8 :                 tmp = strchr_m(srvPrinc, '/');
     510           8 :                 if (tmp == NULL) {
     511           4 :                         my_fqdn = ads_get_dnshostname(ads, frame, lp_netbios_name());
     512           4 :                         if (!my_fqdn) {
     513           0 :                                 DBG_ERR("unable to determine machine account's dns name in "
     514             :                                         "AD!\n");
     515           0 :                                 ret = -1;
     516           0 :                                 goto out;
     517             :                         }
     518             :                 }
     519             : 
     520           8 :                 ok = service_or_spn_to_kerberos_princ(frame,
     521             :                                                       srvPrinc,
     522             :                                                       my_fqdn,
     523             :                                                       &princ_s,
     524             :                                                       &short_princ_s);
     525           8 :                 if (!ok) {
     526           0 :                         ret = -1;
     527           0 :                         goto out;
     528             :                 }
     529             :         }
     530             : 
     531          12 :         ret = smb_krb5_parse_name(context, princ_s, &princ);
     532          12 :         if (ret) {
     533           0 :                 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
     534             :                           "failed (%s)\n", princ_s, error_message(ret)));
     535           0 :                 goto out;
     536             :         }
     537             : 
     538          12 :         if (short_princ_s != NULL) {
     539           4 :                 ret = smb_krb5_parse_name(context, short_princ_s, &short_princ);
     540           4 :                 if (ret) {
     541           0 :                         DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
     542             :                                   "failed (%s)\n", short_princ_s, error_message(ret)));
     543           0 :                         goto out;
     544             :                 }
     545             :         }
     546             : 
     547             :         /* Seek and delete old keytab entries */
     548          12 :         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
     549             :                                                       keytab,
     550             :                                                       false, /* keep_old_kvno */
     551             :                                                       -1,
     552             :                                                       false, /* enctype_only */
     553             :                                                       ENCTYPE_NULL,
     554             :                                                       princ_s,
     555             :                                                       princ,
     556             :                                                       false); /* flush */
     557          12 :         if (ret) {
     558           0 :                 goto out;
     559             :         }
     560             : 
     561          12 :         if (short_princ_s == NULL) {
     562           8 :                 goto out;
     563             :         }
     564             : 
     565             :         /* Seek and delete old keytab entries */
     566           4 :         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
     567             :                                                       keytab,
     568             :                                                       false, /* keep_old_kvno */
     569             :                                                       -1,
     570             :                                                       false, /* enctype_only */
     571             :                                                       ENCTYPE_NULL,
     572             :                                                       short_princ_s,
     573             :                                                       short_princ,
     574             :                                                       false); /* flush */
     575           4 :         if (ret) {
     576           0 :                 goto out;
     577             :         }
     578             : 
     579           4 : out:
     580          12 :         if (princ) {
     581          12 :                 krb5_free_principal(context, princ);
     582             :         }
     583          12 :         if (short_princ) {
     584           4 :                 krb5_free_principal(context, short_princ);
     585             :         }
     586          12 :         if (keytab) {
     587          12 :                 krb5_kt_close(context, keytab);
     588             :         }
     589          12 :         if (context) {
     590          12 :                 krb5_free_context(context);
     591             :         }
     592          12 :         TALLOC_FREE(frame);
     593          12 :         return ret;
     594             : }
     595             : 
     596             : /**********************************************************************
     597             :  Flushes all entries from the system keytab.
     598             : ***********************************************************************/
     599             : 
     600           0 : int ads_keytab_flush(ADS_STRUCT *ads)
     601             : {
     602           0 :         krb5_error_code ret = 0;
     603           0 :         krb5_context context = NULL;
     604           0 :         krb5_keytab keytab = NULL;
     605           0 :         ADS_STATUS aderr;
     606             : 
     607           0 :         ret = smb_krb5_init_context_common(&context);
     608           0 :         if (ret) {
     609           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     610             :                         error_message(ret));
     611           0 :                 return ret;
     612             :         }
     613             : 
     614           0 :         ret = ads_keytab_open(context, &keytab);
     615           0 :         if (ret != 0) {
     616           0 :                 goto out;
     617             :         }
     618             : 
     619             :         /* Seek and delete all old keytab entries */
     620           0 :         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
     621             :                                                       keytab,
     622             :                                                       false, /* keep_old_kvno */
     623             :                                                       -1,
     624             :                                                       false, /* enctype_only */
     625             :                                                       ENCTYPE_NULL,
     626             :                                                       NULL,
     627             :                                                       NULL,
     628             :                                                       true); /* flush */
     629           0 :         if (ret) {
     630           0 :                 goto out;
     631             :         }
     632             : 
     633           0 :         aderr = ads_clear_service_principal_names(ads, lp_netbios_name());
     634           0 :         if (!ADS_ERR_OK(aderr)) {
     635           0 :                 DEBUG(1, (__location__ ": Error while clearing service "
     636             :                           "principal listings in LDAP.\n"));
     637           0 :                 ret = -1;
     638           0 :                 goto out;
     639             :         }
     640             : 
     641           0 : out:
     642           0 :         if (keytab) {
     643           0 :                 krb5_kt_close(context, keytab);
     644             :         }
     645           0 :         if (context) {
     646           0 :                 krb5_free_context(context);
     647             :         }
     648           0 :         return ret;
     649             : }
     650             : 
     651             : /**********************************************************************
     652             :  Adds all the required service principals to the system keytab.
     653             : ***********************************************************************/
     654             : 
     655           8 : int ads_keytab_create_default(ADS_STRUCT *ads)
     656             : {
     657           8 :         krb5_error_code ret = 0;
     658           8 :         krb5_context context = NULL;
     659           8 :         krb5_keytab keytab = NULL;
     660           8 :         krb5_kt_cursor cursor = {0};
     661           8 :         krb5_keytab_entry kt_entry = {0};
     662           0 :         krb5_kvno kvno;
     663           8 :         size_t found = 0;
     664           0 :         char *sam_account_name, *upn;
     665           8 :         char **oldEntries = NULL, *princ_s[26];
     666           0 :         TALLOC_CTX *frame;
     667           0 :         char *machine_name;
     668           0 :         char **spn_array;
     669           0 :         size_t num_spns;
     670           0 :         size_t i;
     671           8 :         bool ok = false;
     672           0 :         ADS_STATUS status;
     673             : 
     674           8 :         ZERO_STRUCT(kt_entry);
     675           8 :         ZERO_STRUCT(cursor);
     676             : 
     677           8 :         frame = talloc_stackframe();
     678           8 :         if (frame == NULL) {
     679           0 :                 ret = -1;
     680           0 :                 goto done;
     681             :         }
     682             : 
     683           8 :         status = ads_get_service_principal_names(frame,
     684             :                                                  ads,
     685             :                                                  lp_netbios_name(),
     686             :                                                  &spn_array,
     687             :                                                  &num_spns);
     688           8 :         if (!ADS_ERR_OK(status)) {
     689           0 :                 ret = -1;
     690           0 :                 goto done;
     691             :         }
     692             : 
     693          48 :         for (i = 0; i < num_spns; i++) {
     694           0 :                 char *srv_princ;
     695           0 :                 char *p;
     696             : 
     697          40 :                 srv_princ = strlower_talloc(frame, spn_array[i]);
     698          40 :                 if (srv_princ == NULL) {
     699           0 :                         ret = -1;
     700           0 :                         goto done;
     701             :                 }
     702             : 
     703          40 :                 p = strchr_m(srv_princ, '/');
     704          40 :                 if (p == NULL) {
     705           0 :                         continue;
     706             :                 }
     707          40 :                 p[0] = '\0';
     708             : 
     709             :                 /* Add the SPNs found on the DC */
     710          40 :                 ret = ads_keytab_add_entry(ads, srv_princ, false);
     711          40 :                 if (ret != 0) {
     712           0 :                         DEBUG(1, ("ads_keytab_add_entry failed while "
     713             :                                   "adding '%s' principal.\n",
     714             :                                   spn_array[i]));
     715           0 :                         goto done;
     716             :                 }
     717             :         }
     718             : 
     719             : #if 0   /* don't create the CIFS/... keytab entries since no one except smbd
     720             :            really needs them and we will fall back to verifying against
     721             :            secrets.tdb */
     722             : 
     723             :         ret = ads_keytab_add_entry(ads, "cifs", false));
     724             :         if (ret != 0 ) {
     725             :                 DEBUG(1, (__location__ ": ads_keytab_add_entry failed while "
     726             :                           "adding 'cifs'.\n"));
     727             :                 return ret;
     728             :         }
     729             : #endif
     730             : 
     731           8 :         memset(princ_s, '\0', sizeof(princ_s));
     732             : 
     733           8 :         ret = smb_krb5_init_context_common(&context);
     734           8 :         if (ret) {
     735           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     736             :                         error_message(ret));
     737           0 :                 goto done;
     738             :         }
     739             : 
     740           8 :         machine_name = talloc_strdup(frame, lp_netbios_name());
     741           8 :         if (!machine_name) {
     742           0 :                 ret = -1;
     743           0 :                 goto done;
     744             :         }
     745             : 
     746             :         /* now add the userPrincipalName and sAMAccountName entries */
     747           8 :         ok = ads_has_samaccountname(ads, frame, machine_name);
     748           8 :         if (!ok) {
     749           0 :                 DEBUG(0, (__location__ ": unable to determine machine "
     750             :                           "account's name in AD!\n"));
     751           0 :                 ret = -1;
     752           0 :                 goto done;
     753             :         }
     754             : 
     755             :         /*
     756             :          * append '$' to netbios name so 'ads_keytab_add_entry' recognises
     757             :          * it as a machine account rather than a service or Windows SPN.
     758             :          */
     759           8 :         sam_account_name = talloc_asprintf(frame, "%s$",machine_name);
     760           8 :         if (sam_account_name == NULL) {
     761           0 :                 ret = -1;
     762           0 :                 goto done;
     763             :         }
     764             :         /* upper case the sAMAccountName to make it easier for apps to
     765             :            know what case to use in the keytab file */
     766           8 :         if (!strupper_m(sam_account_name)) {
     767           0 :                 ret = -1;
     768           0 :                 goto done;
     769             :         }
     770             : 
     771           8 :         ret = ads_keytab_add_entry(ads, sam_account_name, false);
     772           8 :         if (ret != 0) {
     773           0 :                 DEBUG(1, (__location__ ": ads_keytab_add_entry() failed "
     774             :                           "while adding sAMAccountName (%s)\n",
     775             :                           sam_account_name));
     776           0 :                 goto done;
     777             :         }
     778             : 
     779             :         /* remember that not every machine account will have a upn */
     780           8 :         upn = ads_get_upn(ads, frame, machine_name);
     781           8 :         if (upn) {
     782           2 :                 ret = ads_keytab_add_entry(ads, upn, false);
     783           2 :                 if (ret != 0) {
     784           0 :                         DEBUG(1, (__location__ ": ads_keytab_add_entry() "
     785             :                                   "failed while adding UPN (%s)\n", upn));
     786           0 :                         goto done;
     787             :                 }
     788             :         }
     789             : 
     790             :         /* Now loop through the keytab and update any other existing entries */
     791           8 :         kvno = (krb5_kvno)ads_get_machine_kvno(ads, machine_name);
     792           8 :         if (kvno == (krb5_kvno)-1) {
     793           0 :                 DEBUG(1, (__location__ ": ads_get_machine_kvno() failed to "
     794             :                           "determine the system's kvno.\n"));
     795           0 :                 goto done;
     796             :         }
     797             : 
     798           8 :         DEBUG(3, (__location__ ": Searching for keytab entries to preserve "
     799             :                   "and update.\n"));
     800             : 
     801           8 :         ret = ads_keytab_open(context, &keytab);
     802           8 :         if (ret != 0) {
     803           0 :                 goto done;
     804             :         }
     805             : 
     806           8 :         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
     807           8 :         if (ret != KRB5_KT_END && ret != ENOENT ) {
     808         212 :                 while ((ret = krb5_kt_next_entry(context, keytab,
     809         212 :                                                  &kt_entry, &cursor)) == 0) {
     810         204 :                         smb_krb5_kt_free_entry(context, &kt_entry);
     811         204 :                         ZERO_STRUCT(kt_entry);
     812         204 :                         found++;
     813             :                 }
     814             :         }
     815           8 :         krb5_kt_end_seq_get(context, keytab, &cursor);
     816           8 :         ZERO_STRUCT(cursor);
     817             : 
     818             :         /*
     819             :          * Hmmm. There is no "rewind" function for the keytab. This means we
     820             :          * have a race condition where someone else could add entries after
     821             :          * we've counted them. Re-open asap to minimise the race. JRA.
     822             :          */
     823           8 :         DEBUG(3, (__location__ ": Found %zd entries in the keytab.\n", found));
     824           8 :         if (!found) {
     825           0 :                 goto done;
     826             :         }
     827             : 
     828           8 :         oldEntries = talloc_zero_array(frame, char *, found + 1);
     829           8 :         if (!oldEntries) {
     830           0 :                 DEBUG(1, (__location__ ": Failed to allocate space to store "
     831             :                           "the old keytab entries (talloc failed?).\n"));
     832           0 :                 ret = -1;
     833           0 :                 goto done;
     834             :         }
     835             : 
     836           8 :         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
     837           8 :         if (ret == KRB5_KT_END || ret == ENOENT) {
     838           0 :                 krb5_kt_end_seq_get(context, keytab, &cursor);
     839           0 :                 ZERO_STRUCT(cursor);
     840           0 :                 goto done;
     841             :         }
     842             : 
     843         212 :         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
     844         204 :                 if (kt_entry.vno != kvno) {
     845           0 :                         char *ktprinc = NULL;
     846           0 :                         char *p;
     847             : 
     848             :                         /* This returns a malloc'ed string in ktprinc. */
     849           0 :                         ret = smb_krb5_unparse_name(oldEntries, context,
     850           0 :                                                     kt_entry.principal,
     851             :                                                     &ktprinc);
     852           0 :                         if (ret) {
     853           0 :                                 DEBUG(1, (__location__
     854             :                                          ": smb_krb5_unparse_name failed "
     855             :                                          "(%s)\n", error_message(ret)));
     856           0 :                                 goto done;
     857             :                         }
     858             :                         /*
     859             :                          * From looking at the krb5 source they don't seem to
     860             :                          * take locale or mb strings into account.
     861             :                          * Maybe this is because they assume utf8 ?
     862             :                          * In this case we may need to convert from utf8 to
     863             :                          * mb charset here ? JRA.
     864             :                          */
     865           0 :                         p = strchr_m(ktprinc, '@');
     866           0 :                         if (p) {
     867           0 :                                 *p = '\0';
     868             :                         }
     869             : 
     870           0 :                         p = strchr_m(ktprinc, '/');
     871           0 :                         if (p) {
     872           0 :                                 *p = '\0';
     873             :                         }
     874           0 :                         for (i = 0; i < found; i++) {
     875           0 :                                 if (!oldEntries[i]) {
     876           0 :                                         oldEntries[i] = ktprinc;
     877           0 :                                         break;
     878             :                                 }
     879           0 :                                 if (!strcmp(oldEntries[i], ktprinc)) {
     880           0 :                                         TALLOC_FREE(ktprinc);
     881           0 :                                         break;
     882             :                                 }
     883             :                         }
     884           0 :                         if (i == found) {
     885           0 :                                 TALLOC_FREE(ktprinc);
     886             :                         }
     887             :                 }
     888         204 :                 smb_krb5_kt_free_entry(context, &kt_entry);
     889         204 :                 ZERO_STRUCT(kt_entry);
     890             :         }
     891           8 :         krb5_kt_end_seq_get(context, keytab, &cursor);
     892           8 :         ZERO_STRUCT(cursor);
     893             : 
     894           8 :         ret = 0;
     895           8 :         for (i = 0; oldEntries[i]; i++) {
     896           0 :                 ret |= ads_keytab_add_entry(ads, oldEntries[i], false);
     897           0 :                 TALLOC_FREE(oldEntries[i]);
     898             :         }
     899             : 
     900           8 : done:
     901           8 :         TALLOC_FREE(oldEntries);
     902           8 :         TALLOC_FREE(frame);
     903             : 
     904           8 :         if (context) {
     905           8 :                 if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
     906           0 :                         smb_krb5_kt_free_entry(context, &kt_entry);
     907             :                 }
     908           8 :                 if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
     909           0 :                         krb5_kt_end_seq_get(context, keytab, &cursor);
     910             :                 }
     911           8 :                 if (keytab) {
     912           8 :                         krb5_kt_close(context, keytab);
     913             :                 }
     914           8 :                 krb5_free_context(context);
     915             :         }
     916           8 :         return ret;
     917             : }
     918             : 
     919             : #endif /* HAVE_ADS */
     920             : 
     921             : /**********************************************************************
     922             :  List system keytab.
     923             : ***********************************************************************/
     924             : 
     925          42 : int ads_keytab_list(const char *keytab_name)
     926             : {
     927          42 :         krb5_error_code ret = 0;
     928          42 :         krb5_context context = NULL;
     929          42 :         krb5_keytab keytab = NULL;
     930           0 :         krb5_kt_cursor cursor;
     931           0 :         krb5_keytab_entry kt_entry;
     932             : 
     933          42 :         ZERO_STRUCT(kt_entry);
     934          42 :         ZERO_STRUCT(cursor);
     935             : 
     936          42 :         ret = smb_krb5_init_context_common(&context);
     937          42 :         if (ret) {
     938           0 :                 DBG_ERR("kerberos init context failed (%s)\n",
     939             :                         error_message(ret));
     940           0 :                 return ret;
     941             :         }
     942             : 
     943          42 :         if (keytab_name == NULL) {
     944             : #ifdef HAVE_ADS
     945          40 :                 ret = ads_keytab_open(context, &keytab);
     946             : #else
     947           0 :                 ret = ENOENT;
     948             : #endif
     949             :         } else {
     950           2 :                 ret = smb_krb5_kt_open(context, keytab_name, False, &keytab);
     951             :         }
     952          42 :         if (ret) {
     953           0 :                 DEBUG(1, ("smb_krb5_kt_open failed (%s)\n",
     954             :                           error_message(ret)));
     955           0 :                 goto out;
     956             :         }
     957             : 
     958          42 :         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
     959          42 :         if (ret) {
     960           0 :                 ZERO_STRUCT(cursor);
     961           0 :                 goto out;
     962             :         }
     963             : 
     964          42 :         printf("Vno  Type                                        Principal\n");
     965             : 
     966         924 :         while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
     967             : 
     968         882 :                 char *princ_s = NULL;
     969         882 :                 char *etype_s = NULL;
     970         882 :                 krb5_enctype enctype = 0;
     971             : 
     972         882 :                 ret = smb_krb5_unparse_name(talloc_tos(), context,
     973         882 :                                             kt_entry.principal, &princ_s);
     974         882 :                 if (ret) {
     975           0 :                         goto out;
     976             :                 }
     977             : 
     978         882 :                 enctype = smb_krb5_kt_get_enctype_from_entry(&kt_entry);
     979             : 
     980         882 :                 ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
     981         882 :                 if (ret &&
     982           0 :                     (asprintf(&etype_s, "UNKNOWN: %d", enctype) == -1)) {
     983           0 :                         TALLOC_FREE(princ_s);
     984           0 :                         goto out;
     985             :                 }
     986             : 
     987         882 :                 printf("%3d  %-43s %s\n", kt_entry.vno, etype_s, princ_s);
     988             : 
     989         882 :                 TALLOC_FREE(princ_s);
     990         882 :                 SAFE_FREE(etype_s);
     991             : 
     992         882 :                 ret = smb_krb5_kt_free_entry(context, &kt_entry);
     993         882 :                 if (ret) {
     994           0 :                         goto out;
     995             :                 }
     996             :         }
     997             : 
     998          42 :         ret = krb5_kt_end_seq_get(context, keytab, &cursor);
     999          42 :         if (ret) {
    1000           0 :                 goto out;
    1001             :         }
    1002             : 
    1003             :         /* Ensure we don't double free. */
    1004          42 :         ZERO_STRUCT(kt_entry);
    1005          42 :         ZERO_STRUCT(cursor);
    1006          42 : out:
    1007             : 
    1008          42 :         if (!all_zero((uint8_t *)&kt_entry, sizeof(kt_entry))) {
    1009           0 :                 smb_krb5_kt_free_entry(context, &kt_entry);
    1010             :         }
    1011          42 :         if (!all_zero((uint8_t *)&cursor, sizeof(cursor)) && keytab) {
    1012           0 :                 krb5_kt_end_seq_get(context, keytab, &cursor);
    1013             :         }
    1014             : 
    1015          42 :         if (keytab) {
    1016          42 :                 krb5_kt_close(context, keytab);
    1017             :         }
    1018          42 :         if (context) {
    1019          42 :                 krb5_free_context(context);
    1020             :         }
    1021          42 :         return ret;
    1022             : }
    1023             : 
    1024             : #endif /* HAVE_KRB5 */

Generated by: LCOV version 1.14