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

Generated by: LCOV version 1.14