LCOV - code coverage report
Current view: top level - source4/libnet - libnet_export_keytab.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 124 196 63.3 %
Date: 2024-05-31 13:13:24 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             : 
      33          60 : static NTSTATUS sdb_kt_copy(TALLOC_CTX *mem_ctx,
      34             :                             struct smb_krb5_context *smb_krb5_context,
      35             :                             struct samba_kdc_db_context *db_ctx,
      36             :                             const char *keytab_name,
      37             :                             const char *principal,
      38             :                             bool keep_stale_entries,
      39             :                             bool include_historic_keys,
      40             :                             const unsigned sdb_flags,
      41             :                             const char **error_string)
      42             : {
      43          60 :         struct sdb_entry sentry = {};
      44           0 :         krb5_keytab keytab;
      45          60 :         krb5_error_code code = 0;
      46          60 :         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
      47          60 :         char *entry_principal = NULL;
      48          60 :         bool copy_one_principal = (principal != NULL);
      49          60 :         bool keys_exported = false;
      50          60 :         krb5_context context = smb_krb5_context->krb5_context;
      51          60 :         TALLOC_CTX *tmp_ctx = NULL;
      52             : 
      53          60 :         code = smb_krb5_kt_open_relative(context,
      54             :                                          keytab_name,
      55             :                                          true, /* write_access */
      56             :                                          &keytab);
      57          60 :         if (code != 0) {
      58           0 :                 *error_string = talloc_asprintf(mem_ctx,
      59             :                                                 "Failed to open keytab: %s",
      60             :                                                 keytab_name);
      61           0 :                 status = NT_STATUS_NO_SUCH_FILE;
      62           0 :                 goto done;
      63             :         }
      64             : 
      65          60 :         if (copy_one_principal) {
      66           0 :                 krb5_principal k5_princ;
      67             : 
      68          50 :                 code = smb_krb5_parse_name(context, principal, &k5_princ);
      69          50 :                 if (code != 0) {
      70           0 :                         *error_string = smb_get_krb5_error_message(context,
      71             :                                                                    code,
      72             :                                                                    mem_ctx);
      73           0 :                         status = NT_STATUS_UNSUCCESSFUL;
      74           0 :                         goto done;
      75             :                 }
      76             : 
      77          50 :                 code = samba_kdc_fetch(context, db_ctx, k5_princ,
      78             :                                        SDB_F_GET_ANY | sdb_flags,
      79             :                                        0, &sentry);
      80             : 
      81          50 :                 krb5_free_principal(context, k5_princ);
      82             :         } else {
      83          10 :                 code = samba_kdc_firstkey(context, db_ctx, sdb_flags, &sentry);
      84             :         }
      85             : 
      86         186 :         for (; code == 0; code = samba_kdc_nextkey(context, db_ctx, sdb_flags, &sentry)) {
      87           0 :                 int i;
      88         176 :                 bool found_previous = false;
      89         176 :                 tmp_ctx = talloc_new(mem_ctx);
      90         176 :                 if (tmp_ctx == NULL) {
      91           0 :                         status = NT_STATUS_NO_MEMORY;
      92           0 :                         goto done;
      93             :                 }
      94             : 
      95         176 :                 code = krb5_unparse_name(context,
      96         176 :                                          sentry.principal,
      97             :                                          &entry_principal);
      98         176 :                 if (code != 0) {
      99           0 :                         *error_string = smb_get_krb5_error_message(context,
     100             :                                                                    code,
     101             :                                                                    mem_ctx);
     102           0 :                         status = NT_STATUS_UNSUCCESSFUL;
     103           0 :                         goto done;
     104             :                 }
     105             : 
     106         176 :                 if (!keep_stale_entries) {
     107          42 :                         code = smb_krb5_remove_obsolete_keytab_entries(mem_ctx,
     108             :                                                                        context,
     109             :                                                                        keytab,
     110             :                                                                        1, &sentry.principal,
     111          21 :                                                                        sentry.kvno,
     112             :                                                                        &found_previous,
     113             :                                                                        error_string);
     114          42 :                         if (code != 0) {
     115           0 :                                 *error_string = talloc_asprintf(mem_ctx,
     116             :                                                                 "Failed to remove old principals from keytab: %s\n",
     117             :                                                                 *error_string);
     118           0 :                                 status = NT_STATUS_UNSUCCESSFUL;
     119           0 :                                 goto done;
     120             :                         }
     121             :                 }
     122             : 
     123             :                 /*
     124             :                  * If this was a gMSA and we did not just read the
     125             :                  * keys directly, then generate them
     126             :                  */
     127         176 :                 if (sentry.skdc_entry->group_managed_service_account
     128          10 :                     && sentry.keys.len == 0) {
     129           2 :                         struct ldb_dn *dn = sentry.skdc_entry->msg->dn;
     130             :                         /*
     131             :                          * for error message only, but we are about to
     132             :                          * destroy the string name, so write this out
     133             :                          * now
     134             :                          */
     135           0 :                         const char *extended_dn =
     136           2 :                                 ldb_dn_get_extended_linearized(mem_ctx,
     137             :                                                                dn,
     138             :                                                                1);
     139             : 
     140             :                         /*
     141             :                          * Modify the DN in the entry (not needed by
     142             :                          * the KDC code any longer) to be minimal, so
     143             :                          * we can search on it over LDAP.
     144             :                          */
     145           2 :                         ldb_dn_minimise(dn);
     146             : 
     147           2 :                         status = smb_krb5_fill_keytab_gmsa_keys(tmp_ctx,
     148             :                                                                 smb_krb5_context,
     149             :                                                                 keytab,
     150             :                                                                 sentry.principal,
     151             :                                                                 db_ctx->samdb,
     152             :                                                                 dn,
     153             :                                                                 include_historic_keys,
     154             :                                                                 error_string);
     155           2 :                         if (NT_STATUS_IS_OK(status)) {
     156           2 :                                 keys_exported = true;
     157           0 :                         } else if (copy_one_principal) {
     158           0 :                                 *error_string = talloc_asprintf(mem_ctx,
     159             :                                                                 "Failed to write gMSA password for %s to keytab: %s\n",
     160             :                                                                 principal,
     161             :                                                                 *error_string);
     162           0 :                                 goto done;
     163           0 :                         } else if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_USER_KEYS)) {
     164           0 :                                 *error_string = talloc_asprintf(mem_ctx,
     165             :                                                                 "Failed to write gMSA password for %s to keytab: %s\n",
     166             :                                                                 extended_dn,
     167             :                                                                 *error_string);
     168           0 :                                 goto done;
     169             :                         }
     170             :                 } else {
     171           0 :                         krb5_keytab_entry kt_entry;
     172         174 :                         ZERO_STRUCT(kt_entry);
     173         174 :                         kt_entry.principal = sentry.principal;
     174         174 :                         kt_entry.vno       = sentry.kvno;
     175             : 
     176         658 :                         for (i = 0; i < sentry.keys.len; i++) {
     177         484 :                                 struct sdb_key *s = &(sentry.keys.val[i]);
     178           0 :                                 krb5_keyblock *keyp;
     179           0 :                                 bool found;
     180             : 
     181         484 :                                 keyp = KRB5_KT_KEY(&kt_entry);
     182             : 
     183         484 :                                 *keyp = s->key;
     184             : 
     185         484 :                                 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
     186             :                                                                          context,
     187             :                                                                          keytab,
     188             :                                                                          &kt_entry,
     189             :                                                                          &found,
     190             :                                                                          error_string);
     191         484 :                                 if (code != 0) {
     192           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     193           0 :                                         *error_string = smb_get_krb5_error_message(context,
     194             :                                                                                    code,
     195             :                                                                                    mem_ctx);
     196           0 :                                         DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
     197             :                                                   code, *error_string));
     198           0 :                                         goto done;
     199             :                                 }
     200             : 
     201         484 :                                 if (found) {
     202         134 :                                         continue;
     203             :                                 }
     204             : 
     205         350 :                                 code = krb5_kt_add_entry(context, keytab, &kt_entry);
     206         350 :                                 if (code != 0) {
     207           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     208           0 :                                         *error_string = smb_get_krb5_error_message(context,
     209             :                                                                                    code,
     210             :                                                                                    mem_ctx);
     211           0 :                                         DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
     212             :                                                   code, *error_string));
     213           0 :                                         goto done;
     214             :                                 }
     215         350 :                                 keys_exported = true;
     216             :                         }
     217         174 :                         kt_entry.vno -= 1;
     218         222 :                         for (i = 0; include_historic_keys && i < sentry.old_keys.len; i++) {
     219          48 :                                 struct sdb_key *s = &(sentry.old_keys.val[i]);
     220           0 :                                 krb5_keyblock *keyp;
     221           0 :                                 bool found;
     222             : 
     223          48 :                                 keyp = KRB5_KT_KEY(&kt_entry);
     224             : 
     225          48 :                                 *keyp = s->key;
     226             : 
     227          48 :                                 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
     228             :                                                                          context,
     229             :                                                                          keytab,
     230             :                                                                          &kt_entry,
     231             :                                                                          &found,
     232             :                                                                          error_string);
     233          48 :                                 if (code != 0) {
     234           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     235           0 :                                         *error_string = smb_get_krb5_error_message(context,
     236             :                                                                                    code,
     237             :                                                                                    mem_ctx);
     238           0 :                                         DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
     239             :                                                   code, *error_string));
     240           0 :                                         goto done;
     241             :                                 }
     242             : 
     243          48 :                                 if (found) {
     244          24 :                                         continue;
     245             :                                 }
     246             : 
     247          24 :                                 code = krb5_kt_add_entry(context, keytab, &kt_entry);
     248          24 :                                 if (code != 0) {
     249           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     250           0 :                                         *error_string = smb_get_krb5_error_message(context,
     251             :                                                                                    code,
     252             :                                                                                    mem_ctx);
     253           0 :                                         DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
     254             :                                                   code, *error_string));
     255           0 :                                         goto done;
     256             :                                 }
     257          24 :                                 keys_exported = true;
     258             :                         }
     259         174 :                         kt_entry.vno -= 1;
     260         192 :                         for (i = 0; include_historic_keys && i < sentry.older_keys.len; i++) {
     261          18 :                                 struct sdb_key *s = &(sentry.older_keys.val[i]);
     262           0 :                                 krb5_keyblock *keyp;
     263           0 :                                 bool found;
     264             : 
     265          18 :                                 keyp = KRB5_KT_KEY(&kt_entry);
     266             : 
     267          18 :                                 *keyp = s->key;
     268             : 
     269          18 :                                 code = smb_krb5_is_exact_entry_in_keytab(mem_ctx,
     270             :                                                                          context,
     271             :                                                                          keytab,
     272             :                                                                          &kt_entry,
     273             :                                                                          &found,
     274             :                                                                          error_string);
     275          18 :                                 if (code != 0) {
     276           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     277           0 :                                         *error_string = smb_get_krb5_error_message(context,
     278             :                                                                                    code,
     279             :                                                                                    mem_ctx);
     280           0 :                                         DEBUG(0, ("smb_krb5_is_exact_entry_in_keytab failed code=%d, error = %s\n",
     281             :                                                   code, *error_string));
     282           0 :                                         goto done;
     283             :                                 }
     284             : 
     285          18 :                                 if (found) {
     286           6 :                                         continue;
     287             :                                 }
     288             : 
     289          12 :                                 code = krb5_kt_add_entry(context, keytab, &kt_entry);
     290          12 :                                 if (code != 0) {
     291           0 :                                         status = NT_STATUS_UNSUCCESSFUL;
     292           0 :                                         *error_string = smb_get_krb5_error_message(context,
     293             :                                                                                    code,
     294             :                                                                                    mem_ctx);
     295           0 :                                         DEBUG(0, ("smb_krb5_kt_add_entry failed code=%d, error = %s\n",
     296             :                                                   code, *error_string));
     297           0 :                                         goto done;
     298             :                                 }
     299          12 :                                 keys_exported = true;
     300             :                         }
     301             :                 }
     302             : 
     303         176 :                 if (copy_one_principal) {
     304          50 :                         break;
     305             :                 }
     306             : 
     307         126 :                 TALLOC_FREE(tmp_ctx);
     308         126 :                 SAFE_FREE(entry_principal);
     309         126 :                 sdb_entry_free(&sentry);
     310             :         }
     311             : 
     312          60 :         if (code != 0 && code != SDB_ERR_NOENTRY) {
     313           0 :                 *error_string = smb_get_krb5_error_message(context,
     314             :                                                            code,
     315             :                                                            mem_ctx);
     316           0 :                 status = NT_STATUS_NO_SUCH_USER;
     317           0 :                 goto done;
     318             :         }
     319             : 
     320          60 :         if (keys_exported == false) {
     321           2 :                 if (keep_stale_entries == false) {
     322           0 :                         *error_string = talloc_asprintf(mem_ctx,
     323             :                                                         "No keys found while exporting %s.  "
     324             :                                                         "Consider connecting to a local sam.ldb, "
     325             :                                                         "only gMSA accounts can be exported over "
     326             :                                                         "LDAP and connecting user needs to be authorized",
     327             :                                                         principal ? principal : "all users in domain");
     328           0 :                         status = NT_STATUS_NO_USER_KEYS;
     329             :                 } else {
     330           2 :                         DBG_NOTICE("No new keys found while exporting %s.  "
     331             :                                    "If new keys were expected, consider connecting "
     332             :                                    "to a local sam.ldb, only gMSA accounts can be exported over "
     333             :                                    "LDAP and connecting user needs to be authorized\n",
     334             :                                    principal ? principal : "all users in domain");
     335           2 :                         status = NT_STATUS_OK;
     336             :                 }
     337             :         } else {
     338          58 :                 status = NT_STATUS_OK;
     339             :         }
     340             : 
     341          60 : done:
     342          60 :         TALLOC_FREE(tmp_ctx);
     343          60 :         SAFE_FREE(entry_principal);
     344          60 :         sdb_entry_free(&sentry);
     345             : 
     346          60 :         return status;
     347             : }
     348             : 
     349          64 : NTSTATUS libnet_export_keytab(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_export_keytab *r)
     350             : {
     351           0 :         krb5_error_code ret;
     352           0 :         struct smb_krb5_context *smb_krb5_context;
     353           0 :         struct samba_kdc_base_context *base_ctx;
     354          64 :         struct samba_kdc_db_context *db_ctx = NULL;
     355          64 :         const char *error_string = NULL;
     356           0 :         unsigned sdb_flags;
     357           0 :         NTSTATUS status;
     358             : 
     359          64 :         bool keep_stale_entries = r->in.keep_stale_entries;
     360             : 
     361          64 :         ret = smb_krb5_init_context(ctx, ctx->lp_ctx, &smb_krb5_context);
     362          64 :         if (ret) {
     363           0 :                 return NT_STATUS_NO_MEMORY;
     364             :         }
     365             : 
     366          64 :         base_ctx = talloc_zero(mem_ctx, struct samba_kdc_base_context);
     367          64 :         if (base_ctx == NULL) {
     368           0 :                 return NT_STATUS_NO_MEMORY;
     369             :         }
     370             : 
     371          64 :         base_ctx->ev_ctx = ctx->event_ctx;
     372          64 :         base_ctx->lp_ctx = ctx->lp_ctx;
     373          64 :         base_ctx->samdb = r->in.samdb;
     374             : 
     375          64 :         status = samba_kdc_setup_db_ctx(mem_ctx, base_ctx, &db_ctx);
     376          64 :         if (!NT_STATUS_IS_OK(status)) {
     377           0 :                 return status;
     378             :         }
     379             : 
     380          64 :         if (r->in.principal != NULL) {
     381          50 :                 DEBUG(0, ("Export one principal to %s\n", r->in.keytab_name));
     382             :         } else {
     383          14 :                 DEBUG(0, ("Export complete keytab to %s\n", r->in.keytab_name));
     384          14 :                 if (!keep_stale_entries) {
     385           0 :                         struct stat st;
     386          10 :                         int stat_ret = stat(r->in.keytab_name, &st);
     387          10 :                         if (stat_ret == -1 && errno == ENOENT) {
     388             :                                 /* continue */
     389           4 :                         } else if (stat_ret == -1) {
     390           2 :                                 int errno_save = errno;
     391           0 :                                 r->out.error_string
     392           2 :                                         = talloc_asprintf(mem_ctx,
     393             :                                                           "Failure checking if keytab export location %s is an existing file: %s",
     394             :                                                           r->in.keytab_name,
     395             :                                                           strerror(errno_save));
     396           4 :                                 return map_nt_error_from_unix_common(errno_save);
     397             :                         } else {
     398           0 :                                 r->out.error_string
     399           2 :                                         = talloc_asprintf(mem_ctx,
     400             :                                                           "Refusing to export keytab to existing file %s",
     401             :                                                           r->in.keytab_name);
     402           2 :                                 return NT_STATUS_OBJECT_NAME_EXISTS;
     403             :                         }
     404             : 
     405             :                         /*
     406             :                          * No point looking for old
     407             :                          * keys in a empty file
     408             :                          */
     409           6 :                         keep_stale_entries = true;
     410             :                 }
     411             :         }
     412             : 
     413          60 :         sdb_flags = r->in.as_for_AS_REQ ? SDB_F_FOR_AS_REQ : SDB_F_ADMIN_DATA;
     414             : 
     415          60 :         status = sdb_kt_copy(mem_ctx,
     416             :                              smb_krb5_context,
     417             :                              db_ctx,
     418             :                              r->in.keytab_name,
     419             :                              r->in.principal,
     420             :                              keep_stale_entries,
     421          60 :                              !r->in.only_current_keys,
     422             :                              sdb_flags,
     423          60 :                              &error_string);
     424             : 
     425          60 :         talloc_free(db_ctx);
     426          60 :         talloc_free(base_ctx);
     427             : 
     428          60 :         if (!NT_STATUS_IS_OK(status)) {
     429           0 :                 r->out.error_string = error_string;
     430             :         }
     431             : 
     432          60 :         return status;
     433             : }

Generated by: LCOV version 1.14