LCOV - code coverage report
Current view: top level - source4/libnet - libnet_export_keytab.c (source / functions) Hit Total Coverage
Test: coverage report for master 469b22b8 Lines: 132 211 62.6 %
Date: 2024-06-10 12:05:21 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
       5             :    Copyright (C) Andreas Schneider <asn@samba.org> 2016
       6             : 
       7             :    This program is free software; you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation; either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    This program is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "includes.h"
      22             : #include "system/kerberos.h"
      23             : #include "auth/credentials/credentials.h"
      24             : #include "auth/kerberos/kerberos.h"
      25             : #include "auth/kerberos/kerberos_credentials.h"
      26             : #include "auth/kerberos/kerberos_util.h"
      27             : #include "auth/kerberos/kerberos_srv_keytab.h"
      28             : #include "kdc/samba_kdc.h"
      29             : #include "libnet/libnet_export_keytab.h"
      30             : #include "kdc/db-glue.h"
      31             : #include "kdc/sdb.h"
      32             : #include "dsdb/gmsa/util.h"
      33             : 
      34          60 : static NTSTATUS sdb_kt_copy(TALLOC_CTX *mem_ctx,
      35             :                             struct smb_krb5_context *smb_krb5_context,
      36             :                             struct samba_kdc_db_context *db_ctx,
      37             :                             const char *keytab_name,
      38             :                             const char *principal,
      39             :                             bool keep_stale_entries,
      40             :                             bool include_historic_keys,
      41             :                             const unsigned sdb_flags,
      42             :                             const char **error_string)
      43             : {
      44          60 :         struct sdb_entry sentry = {};
      45           0 :         krb5_keytab keytab;
      46          60 :         krb5_error_code code = 0;
      47          60 :         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
      48          60 :         char *entry_principal = NULL;
      49          60 :         bool copy_one_principal = (principal != NULL);
      50          60 :         bool keys_exported = false;
      51          60 :         krb5_context context = smb_krb5_context->krb5_context;
      52          60 :         TALLOC_CTX *tmp_ctx = NULL;
      53             : 
      54          60 :         code = smb_krb5_kt_open_relative(context,
      55             :                                          keytab_name,
      56             :                                          true, /* write_access */
      57             :                                          &keytab);
      58          60 :         if (code != 0) {
      59           0 :                 *error_string = talloc_asprintf(mem_ctx,
      60             :                                                 "Failed to open keytab: %s",
      61             :                                                 keytab_name);
      62           0 :                 status = NT_STATUS_NO_SUCH_FILE;
      63           0 :                 goto done;
      64             :         }
      65             : 
      66          60 :         if (copy_one_principal) {
      67           0 :                 krb5_principal k5_princ;
      68             : 
      69          50 :                 code = smb_krb5_parse_name(context, principal, &k5_princ);
      70          50 :                 if (code != 0) {
      71           0 :                         *error_string = smb_get_krb5_error_message(context,
      72             :                                                                    code,
      73             :                                                                    mem_ctx);
      74           0 :                         status = NT_STATUS_UNSUCCESSFUL;
      75           0 :                         goto done;
      76             :                 }
      77             : 
      78          50 :                 code = samba_kdc_fetch(context, db_ctx, k5_princ,
      79             :                                        SDB_F_GET_ANY | sdb_flags,
      80             :                                        0, &sentry);
      81             : 
      82          50 :                 krb5_free_principal(context, k5_princ);
      83             :         } else {
      84          10 :                 code = samba_kdc_firstkey(context, db_ctx, sdb_flags, &sentry);
      85             :         }
      86             : 
      87         186 :         for (; code == 0; code = samba_kdc_nextkey(context, db_ctx, sdb_flags, &sentry)) {
      88           0 :                 int i;
      89         176 :                 bool found_previous = false;
      90         176 :                 tmp_ctx = talloc_new(mem_ctx);
      91         176 :                 if (tmp_ctx == NULL) {
      92           0 :                         status = NT_STATUS_NO_MEMORY;
      93           0 :                         goto done;
      94             :                 }
      95             : 
      96         176 :                 code = krb5_unparse_name(context,
      97         176 :                                          sentry.principal,
      98             :                                          &entry_principal);
      99         176 :                 if (code != 0) {
     100           0 :                         *error_string = smb_get_krb5_error_message(context,
     101             :                                                                    code,
     102             :                                                                    mem_ctx);
     103           0 :                         status = NT_STATUS_UNSUCCESSFUL;
     104           0 :                         goto done;
     105             :                 }
     106             : 
     107         176 :                 if (!keep_stale_entries) {
     108          42 :                         code = smb_krb5_remove_obsolete_keytab_entries(mem_ctx,
     109             :                                                                        context,
     110             :                                                                        keytab,
     111             :                                                                        1, &sentry.principal,
     112          21 :                                                                        sentry.kvno,
     113             :                                                                        &found_previous,
     114             :                                                                        error_string);
     115          42 :                         if (code != 0) {
     116           0 :                                 *error_string = talloc_asprintf(mem_ctx,
     117             :                                                                 "Failed to remove old principals from keytab: %s\n",
     118             :                                                                 *error_string);
     119           0 :                                 status = NT_STATUS_UNSUCCESSFUL;
     120           0 :                                 goto done;
     121             :                         }
     122             :                 }
     123             : 
     124             :                 /*
     125             :                  * If this was a gMSA and we did not just read the
     126             :                  * keys directly, then generate them
     127             :                  */
     128         176 :                 if (sentry.skdc_entry->group_managed_service_account
     129          10 :                     && sentry.keys.len == 0) {
     130           2 :                         struct ldb_dn *dn = sentry.skdc_entry->msg->dn;
     131             :                         /*
     132             :                          * for error message only, but we are about to
     133             :                          * destroy the string name, so write this out
     134             :                          * now
     135             :                          */
     136           0 :                         const char *extended_dn =
     137           2 :                                 ldb_dn_get_extended_linearized(mem_ctx,
     138             :                                                                dn,
     139             :                                                                1);
     140             : 
     141             :                         /*
     142             :                          * Modify the DN in the entry (not needed by
     143             :                          * the KDC code any longer) to be minimal, so
     144             :                          * we can search on it over LDAP.
     145             :                          */
     146           2 :                         ldb_dn_minimise(dn);
     147             : 
     148           2 :                         status = smb_krb5_fill_keytab_gmsa_keys(tmp_ctx,
     149             :                                                                 smb_krb5_context,
     150             :                                                                 keytab,
     151             :                                                                 sentry.principal,
     152             :                                                                 db_ctx->samdb,
     153             :                                                                 dn,
     154             :                                                                 include_historic_keys,
     155             :                                                                 error_string);
     156           2 :                         if (NT_STATUS_IS_OK(status)) {
     157           2 :                                 keys_exported = true;
     158           0 :                         } else if (copy_one_principal) {
     159           0 :                                 *error_string = talloc_asprintf(mem_ctx,
     160             :                                                                 "Failed to write gMSA password for %s to keytab: %s\n",
     161             :                                                                 principal,
     162             :                                                                 *error_string);
     163           0 :                                 goto done;
     164           0 :                         } else if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_USER_KEYS)) {
     165           0 :                                 *error_string = talloc_asprintf(mem_ctx,
     166             :                                                                 "Failed to write gMSA password for %s to keytab: %s\n",
     167             :                                                                 extended_dn,
     168             :                                                                 *error_string);
     169           0 :                                 goto done;
     170             :                         }
     171             :                 } else {
     172           0 :                         krb5_keytab_entry kt_entry;
     173         174 :                         ZERO_STRUCT(kt_entry);
     174         174 :                         kt_entry.principal = sentry.principal;
     175         174 :                         kt_entry.vno       = sentry.kvno;
     176             : 
     177         658 :                         for (i = 0; i < sentry.keys.len; i++) {
     178         484 :                                 struct sdb_key *s = &(sentry.keys.val[i]);
     179           0 :                                 krb5_keyblock *keyp;
     180           0 :                                 bool found;
     181             : 
     182         484 :                                 keyp = KRB5_KT_KEY(&kt_entry);
     183             : 
     184         484 :                                 *keyp = s->key;
     185             : 
     186         484 :                                 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
     187             :                                                                          context,
     188             :                                                                          keytab,
     189             :                                                                          &kt_entry,
     190             :                                                                          &found,
     191             :                                                                          error_string);
     192         484 :                                 if (code != 0) {
     193           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     194           0 :                                         *error_string = smb_get_krb5_error_message(context,
     195             :                                                                                    code,
     196             :                                                                                    mem_ctx);
     197           0 :                                         DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
     198             :                                                   code, *error_string));
     199           0 :                                         goto done;
     200             :                                 }
     201             : 
     202         484 :                                 if (found) {
     203         134 :                                         continue;
     204             :                                 }
     205             : 
     206         350 :                                 code = krb5_kt_add_entry(context, keytab, &kt_entry);
     207         350 :                                 if (code != 0) {
     208           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     209           0 :                                         *error_string = smb_get_krb5_error_message(context,
     210             :                                                                                    code,
     211             :                                                                                    mem_ctx);
     212           0 :                                         DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
     213             :                                                   code, *error_string));
     214           0 :                                         goto done;
     215             :                                 }
     216         350 :                                 keys_exported = true;
     217             :                         }
     218         174 :                         kt_entry.vno -= 1;
     219         222 :                         for (i = 0; include_historic_keys && i < sentry.old_keys.len; i++) {
     220          48 :                                 struct sdb_key *s = &(sentry.old_keys.val[i]);
     221           0 :                                 krb5_keyblock *keyp;
     222           0 :                                 bool found;
     223             : 
     224          48 :                                 keyp = KRB5_KT_KEY(&kt_entry);
     225             : 
     226          48 :                                 *keyp = s->key;
     227             : 
     228          48 :                                 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
     229             :                                                                          context,
     230             :                                                                          keytab,
     231             :                                                                          &kt_entry,
     232             :                                                                          &found,
     233             :                                                                          error_string);
     234          48 :                                 if (code != 0) {
     235           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     236           0 :                                         *error_string = smb_get_krb5_error_message(context,
     237             :                                                                                    code,
     238             :                                                                                    mem_ctx);
     239           0 :                                         DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
     240             :                                                   code, *error_string));
     241           0 :                                         goto done;
     242             :                                 }
     243             : 
     244          48 :                                 if (found) {
     245          24 :                                         continue;
     246             :                                 }
     247             : 
     248          24 :                                 code = krb5_kt_add_entry(context, keytab, &kt_entry);
     249          24 :                                 if (code != 0) {
     250           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     251           0 :                                         *error_string = smb_get_krb5_error_message(context,
     252             :                                                                                    code,
     253             :                                                                                    mem_ctx);
     254           0 :                                         DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
     255             :                                                   code, *error_string));
     256           0 :                                         goto done;
     257             :                                 }
     258          24 :                                 keys_exported = true;
     259             :                         }
     260         174 :                         kt_entry.vno -= 1;
     261         192 :                         for (i = 0; include_historic_keys && i < sentry.older_keys.len; i++) {
     262          18 :                                 struct sdb_key *s = &(sentry.older_keys.val[i]);
     263           0 :                                 krb5_keyblock *keyp;
     264           0 :                                 bool found;
     265             : 
     266          18 :                                 keyp = KRB5_KT_KEY(&kt_entry);
     267             : 
     268          18 :                                 *keyp = s->key;
     269             : 
     270          18 :                                 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
     271             :                                                                          context,
     272             :                                                                          keytab,
     273             :                                                                          &kt_entry,
     274             :                                                                          &found,
     275             :                                                                          error_string);
     276          18 :                                 if (code != 0) {
     277           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     278           0 :                                         *error_string = smb_get_krb5_error_message(context,
     279             :                                                                                    code,
     280             :                                                                                    mem_ctx);
     281           0 :                                         DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
     282             :                                                   code, *error_string));
     283           0 :                                         goto done;
     284             :                                 }
     285             : 
     286          18 :                                 if (found) {
     287           6 :                                         continue;
     288             :                                 }
     289             : 
     290          12 :                                 code = krb5_kt_add_entry(context, keytab, &kt_entry);
     291          12 :                                 if (code != 0) {
     292           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     293           0 :                                         *error_string = smb_get_krb5_error_message(context,
     294             :                                                                                    code,
     295             :                                                                                    mem_ctx);
     296           0 :                                         DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
     297             :                                                   code, *error_string));
     298           0 :                                         goto done;
     299             :                                 }
     300          12 :                                 keys_exported = true;
     301             :                         }
     302             :                 }
     303             : 
     304         176 :                 if (copy_one_principal) {
     305          50 :                         break;
     306             :                 }
     307             : 
     308         126 :                 TALLOC_FREE(tmp_ctx);
     309         126 :                 SAFE_FREE(entry_principal);
     310         126 :                 sdb_entry_free(&sentry);
     311             :         }
     312             : 
     313          60 :         if (code != 0 && code != SDB_ERR_NOENTRY) {
     314           0 :                 *error_string = smb_get_krb5_error_message(context,
     315             :                                                            code,
     316             :                                                            mem_ctx);
     317           0 :                 status = NT_STATUS_NO_SUCH_USER;
     318           0 :                 goto done;
     319             :         }
     320             : 
     321          60 :         if (keys_exported == false) {
     322           2 :                 if (keep_stale_entries == false) {
     323           0 :                         *error_string = talloc_asprintf(mem_ctx,
     324             :                                                         "No keys found while exporting %s.  "
     325             :                                                         "Consider connecting to a local sam.ldb, "
     326             :                                                         "only gMSA accounts can be exported over "
     327             :                                                         "LDAP and connecting user needs to be authorized",
     328             :                                                         principal ? principal : "all users in domain");
     329           0 :                         status = NT_STATUS_NO_USER_KEYS;
     330             :                 } else {
     331           2 :                         DBG_NOTICE("No new keys found while exporting %s.  "
     332             :                                    "If new keys were expected, consider connecting "
     333             :                                    "to a local sam.ldb, only gMSA accounts can be exported over "
     334             :                                    "LDAP and connecting user needs to be authorized\n",
     335             :                                    principal ? principal : "all users in domain");
     336           2 :                         status = NT_STATUS_OK;
     337             :                 }
     338             :         } else {
     339          58 :                 status = NT_STATUS_OK;
     340             :         }
     341             : 
     342          60 : done:
     343          60 :         TALLOC_FREE(tmp_ctx);
     344          60 :         SAFE_FREE(entry_principal);
     345          60 :         sdb_entry_free(&sentry);
     346             : 
     347          60 :         return status;
     348             : }
     349             : 
     350          64 : NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
     351             : {
     352           0 :         krb5_error_code ret;
     353           0 :         struct smb_krb5_context *smb_krb5_context;
     354           0 :         struct samba_kdc_base_context *base_ctx;
     355          64 :         struct samba_kdc_db_context *db_ctx = NULL;
     356          64 :         const char *error_string = NULL;
     357           0 :         unsigned sdb_flags;
     358           0 :         NTSTATUS status;
     359             : 
     360          64 :         bool keep_stale_entries = r->in.keep_stale_entries;
     361             : 
     362          64 :         ret = smb_krb5_init_context(ctx, ctx->lp_ctx, &smb_krb5_context);
     363          64 :         if (ret) {
     364           0 :                 return NT_STATUS_NO_MEMORY;
     365             :         }
     366             : 
     367          64 :         base_ctx = talloc_zero(mem_ctx, struct samba_kdc_base_context);
     368          64 :         if (base_ctx == NULL) {
     369           0 :                 return NT_STATUS_NO_MEMORY;
     370             :         }
     371             : 
     372          64 :         base_ctx->ev_ctx = ctx->event_ctx;
     373          64 :         base_ctx->lp_ctx = ctx->lp_ctx;
     374          64 :         base_ctx->samdb = r->in.samdb;
     375          64 :         if (base_ctx->samdb != NULL) {
     376          24 :                 base_ctx->current_nttime_ull = talloc_get_type(
     377             :                         ldb_get_opaque(base_ctx->samdb, DSDB_GMSA_TIME_OPAQUE), unsigned long long);
     378             :         }
     379             : 
     380             :         /*
     381             :          * If the caller hasn't set a fixed time, or a samdb, set up
     382             :          * the pointer for the opaque and set to the current time
     383             :          */
     384          64 :         if (base_ctx->current_nttime_ull == NULL) {
     385           0 :                 bool time_ok;
     386           0 :                 NTTIME current_nttime;
     387             : 
     388          60 :                 base_ctx->current_nttime_ull = talloc_zero(base_ctx, unsigned long long);
     389          60 :                 if (base_ctx->current_nttime_ull == NULL) {
     390           0 :                         r->out.error_string = NULL;
     391           0 :                         return NT_STATUS_NO_MEMORY;
     392             :                 }
     393             : 
     394          60 :                 time_ok = gmsa_current_time(&current_nttime);
     395             : 
     396          60 :                 if (!time_ok) {
     397             :                         /* This is really quite unlikely */
     398           0 :                         r->out.error_string
     399           0 :                                 = talloc_asprintf(mem_ctx,
     400             :                                                   "Failed to get current time to check "
     401             :                                                   "time-dependent keys against for export");
     402           0 :                         return NT_STATUS_UNSUCCESSFUL;
     403             :                 }
     404          60 :                 *base_ctx->current_nttime_ull = current_nttime;
     405             :         }
     406             : 
     407          64 :         status = samba_kdc_setup_db_ctx(mem_ctx, base_ctx, &db_ctx);
     408          64 :         if (!NT_STATUS_IS_OK(status)) {
     409           0 :                 return status;
     410             :         }
     411             : 
     412          64 :         if (r->in.principal != NULL) {
     413          50 :                 DEBUG(0, ("Export one principal to %s\n", r->in.keytab_name));
     414             :         } else {
     415          14 :                 DEBUG(0, ("Export complete keytab to %s\n", r->in.keytab_name));
     416          14 :                 if (!keep_stale_entries) {
     417           0 :                         struct stat st;
     418          10 :                         int stat_ret = stat(r->in.keytab_name, &st);
     419          10 :                         if (stat_ret == -1 && errno == ENOENT) {
     420             :                                 /* continue */
     421           4 :                         } else if (stat_ret == -1) {
     422           2 :                                 int errno_save = errno;
     423           0 :                                 r->out.error_string
     424           2 :                                         = talloc_asprintf(mem_ctx,
     425             :                                                           "Failure checking if keytab export location %s is an existing file: %s",
     426             :                                                           r->in.keytab_name,
     427             :                                                           strerror(errno_save));
     428           4 :                                 return map_nt_error_from_unix_common(errno_save);
     429             :                         } else {
     430           0 :                                 r->out.error_string
     431           2 :                                         = talloc_asprintf(mem_ctx,
     432             :                                                           "Refusing to export keytab to existing file %s",
     433             :                                                           r->in.keytab_name);
     434           2 :                                 return NT_STATUS_OBJECT_NAME_EXISTS;
     435             :                         }
     436             : 
     437             :                         /*
     438             :                          * No point looking for old
     439             :                          * keys in a empty file
     440             :                          */
     441           6 :                         keep_stale_entries = true;
     442             :                 }
     443             :         }
     444             : 
     445          60 :         sdb_flags = r->in.as_for_AS_REQ ? SDB_F_FOR_AS_REQ : SDB_F_ADMIN_DATA;
     446             : 
     447          60 :         status = sdb_kt_copy(mem_ctx,
     448             :                              smb_krb5_context,
     449             :                              db_ctx,
     450             :                              r->in.keytab_name,
     451             :                              r->in.principal,
     452             :                              keep_stale_entries,
     453          60 :                              !r->in.only_current_keys,
     454             :                              sdb_flags,
     455          60 :                              &error_string);
     456             : 
     457          60 :         talloc_free(db_ctx);
     458          60 :         talloc_free(base_ctx);
     459             : 
     460          60 :         if (!NT_STATUS_IS_OK(status)) {
     461           0 :                 r->out.error_string = error_string;
     462             :         }
     463             : 
     464          60 :         return status;
     465             : }

Generated by: LCOV version 1.14