s3:libads: add ads_keytab_delete_entry()
authorStefan Metzmacher <metze@samba.org>
Wed, 26 Oct 2022 09:36:01 +0000 (11:36 +0200)
committerJeremy Allison <jra@samba.org>
Thu, 27 Oct 2022 21:14:43 +0000 (21:14 +0000)
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/libads/ads_proto.h
source3/libads/kerberos_keytab.c

index ce04ac2a2520882540a4ca415abfbd67659e3216..76396aabc344a247ec8a37bc03dc9e2894ae2769 100644 (file)
@@ -62,6 +62,7 @@ void ads_disp_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, struct security_descripto
 
 int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc,
                         bool update_ads);
+int ads_keytab_delete_entry(ADS_STRUCT *ads, const char *srvPrinc);
 int ads_keytab_flush(ADS_STRUCT *ads);
 int ads_keytab_create_default(ADS_STRUCT *ads);
 int ads_keytab_list(const char *keytab_name);
index 00f9a4959209a9e6f8354f1ce9d0579c0c417afb..347879c54a5325b5b138afcf8e7a3a7fe2f1f394 100644 (file)
@@ -457,6 +457,148 @@ out:
        return (int)ret;
 }
 
+/**********************************************************************
+ Delete a single service principal, i.e. 'host' from the system keytab
+***********************************************************************/
+
+int ads_keytab_delete_entry(ADS_STRUCT *ads, const char *srvPrinc)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       krb5_error_code ret = 0;
+       krb5_context context = NULL;
+       krb5_keytab keytab = NULL;
+       char *princ_s = NULL;
+       krb5_principal princ = NULL;
+       char *short_princ_s = NULL;
+       krb5_principal short_princ = NULL;
+       bool ok;
+
+       ret = smb_krb5_init_context_common(&context);
+       if (ret) {
+               DBG_ERR("kerberos init context failed (%s)\n",
+                       error_message(ret));
+               goto out;
+       }
+
+       ret = ads_keytab_open(context, &keytab);
+       if (ret != 0) {
+               goto out;
+       }
+
+       /* Construct our principal */
+       if (strchr_m(srvPrinc, '@')) {
+               /* It's a fully-named principal. */
+               princ_s = talloc_asprintf(frame, "%s", srvPrinc);
+               if (!princ_s) {
+                       ret = -1;
+                       goto out;
+               }
+       } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
+               /* It's the machine account, as used by smbclient clients. */
+               princ_s = talloc_asprintf(frame, "%s@%s",
+                                         srvPrinc, lp_realm());
+               if (!princ_s) {
+                       ret = -1;
+                       goto out;
+               }
+       } else {
+               /*
+                * It's a normal service principal.
+                */
+               char *my_fqdn = NULL;
+               char *tmp = NULL;
+
+               /*
+                * SPN should have '/' otherwise we
+                * need to fallback and find our dnshostname
+                */
+               tmp = strchr_m(srvPrinc, '/');
+               if (tmp == NULL) {
+                       my_fqdn = ads_get_dnshostname(ads, frame, lp_netbios_name());
+                       if (!my_fqdn) {
+                               DBG_ERR("unable to determine machine account's dns name in "
+                                       "AD!\n");
+                               ret = -1;
+                               goto out;
+                       }
+               }
+
+               ok = service_or_spn_to_kerberos_princ(frame,
+                                                     srvPrinc,
+                                                     my_fqdn,
+                                                     &princ_s,
+                                                     &short_princ_s);
+               if (!ok) {
+                       ret = -1;
+                       goto out;
+               }
+       }
+
+       ret = smb_krb5_parse_name(context, princ_s, &princ);
+       if (ret) {
+               DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
+                         "failed (%s)\n", princ_s, error_message(ret)));
+               goto out;
+       }
+
+       if (short_princ_s != NULL) {
+               ret = smb_krb5_parse_name(context, short_princ_s, &short_princ);
+               if (ret) {
+                       DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
+                                 "failed (%s)\n", short_princ_s, error_message(ret)));
+                       goto out;
+               }
+       }
+
+       /* Seek and delete old keytab entries */
+       ret = smb_krb5_kt_seek_and_delete_old_entries(context,
+                                                     keytab,
+                                                     false, /* keep_old_kvno */
+                                                     -1,
+                                                     false, /* enctype_only */
+                                                     ENCTYPE_NULL,
+                                                     princ_s,
+                                                     princ,
+                                                     false); /* flush */
+       if (ret) {
+               goto out;
+       }
+
+       if (short_princ_s == NULL) {
+               goto out;
+       }
+
+       /* Seek and delete old keytab entries */
+       ret = smb_krb5_kt_seek_and_delete_old_entries(context,
+                                                     keytab,
+                                                     false, /* keep_old_kvno */
+                                                     -1,
+                                                     false, /* enctype_only */
+                                                     ENCTYPE_NULL,
+                                                     short_princ_s,
+                                                     short_princ,
+                                                     false); /* flush */
+       if (ret) {
+               goto out;
+       }
+
+out:
+       if (princ) {
+               krb5_free_principal(context, princ);
+       }
+       if (short_princ) {
+               krb5_free_principal(context, short_princ);
+       }
+       if (keytab) {
+               krb5_kt_close(context, keytab);
+       }
+       if (context) {
+               krb5_free_context(context);
+       }
+       TALLOC_FREE(frame);
+       return ret;
+}
+
 /**********************************************************************
  Flushes all entries from the system keytab.
 ***********************************************************************/