From 62d40286ac67faa6b009e121035f92afa1372cf3 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Fri, 4 Dec 2009 16:29:09 -0500 Subject: A utility for removing principals from a keytab. When we un-enroll a client we'll do a bit of cleanup including removing any principals for the IPA realm from /etc/krb5.keytab. This removes principals in 2 ways: - By principal, only entries matching the full principal are removed - By realm. Any principal for that realm is removed This does not change the KDC at all, just removes entries from a file on the client machine. --- ipa-client/Makefile.am | 10 ++ ipa-client/ipa-rmkeytab.c | 218 ++++++++++++++++++++++++++++++++++++++++++ ipa-client/man/Makefile.am | 1 + ipa-client/man/ipa-rmkeytab.1 | 90 +++++++++++++++++ ipa.spec.in | 5 + 5 files changed, 324 insertions(+) create mode 100644 ipa-client/ipa-rmkeytab.c create mode 100644 ipa-client/man/ipa-rmkeytab.1 diff --git a/ipa-client/Makefile.am b/ipa-client/Makefile.am index 9a8b5f690..3f3c13b1a 100644 --- a/ipa-client/Makefile.am +++ b/ipa-client/Makefile.am @@ -22,6 +22,7 @@ INCLUDES = \ sbin_PROGRAMS = \ ipa-getkeytab \ + ipa-rmkeytab \ ipa-join \ $(NULL) @@ -37,6 +38,15 @@ ipa_getkeytab_LDADD = \ $(POPT_LIBS) \ $(NULL) +ipa_rmkeytab_SOURCES = \ + ipa-rmkeytab.c \ + $(NULL) + +ipa_rmkeytab_LDADD = \ + $(KRB5_LIBS) \ + $(POPT_LIBS) \ + $(NULL) + ipa_join_SOURCES = \ config.c \ ipa-join.c \ diff --git a/ipa-client/ipa-rmkeytab.c b/ipa-client/ipa-rmkeytab.c new file mode 100644 index 000000000..d3f1ded37 --- /dev/null +++ b/ipa-client/ipa-rmkeytab.c @@ -0,0 +1,218 @@ +/* Authors: Rob Crittenden + * + * Copyright (C) 2009 Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 only + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +int +remove_principal(krb5_context context, krb5_keytab ktid, const char *principal, int debug) +{ + krb5_error_code krberr; + krb5_keytab_entry entry, entry2; + int rval = 0; + int removed = 0; + + memset(&entry, 0, sizeof(entry)); + krberr = krb5_parse_name(context, principal, &entry.principal); + if (krberr) { + fprintf(stderr, "Unable to parse principal name\n"); + if (debug) + fprintf(stderr, "krb5_parse_name %d: %s\n", krberr, error_message(krberr)); + rval = 4; + goto done; + } + + /* Loop through the keytab and remove all entries with this principal name + * irrespective of the encryption type. A failure to find one after the + * first means we're done. + */ + fprintf(stderr, "Removing principal %s\n", principal); + while (1) { + memset(&entry2, 0, sizeof(entry2)); + krberr = krb5_kt_get_entry(context, ktid, + entry.principal, + 0, + 0, + &entry2); + if (krberr) { + if (removed > 0) + /* not found but we've removed some, we're done */ + break; + if (krberr == ENOENT) { + fprintf(stderr, "Failed to open keytab\n"); + rval = 3; + goto done; + } + fprintf(stderr, "principal not found\n"); + if (debug) + fprintf(stderr, "krb5_kt_get_entry %d: %s\n", krberr, error_message(krberr)); + rval = 5; + break; + } + + krberr = krb5_kt_remove_entry(context, ktid, &entry2); + if (krberr) { + fprintf(stderr, "Unable to remove entry\n"); + if (debug) { + fprintf(stdout, "kvno %d\n", entry2.vno); + fprintf(stderr, "krb5_kt_remove_entry %d: %s\n", krberr, error_message(krberr)); + } + rval = 6; + break; + } + + krb5_free_keytab_entry_contents(context, &entry2); + removed++; + } + + if (entry2.principal) + krb5_free_keytab_entry_contents(context, &entry2); + +done: + + return rval; +} + +int +remove_realm(krb5_context context, krb5_keytab ktid, const char *realm, int debug) +{ + krb5_error_code krberr; + krb5_keytab_entry entry; + krb5_kt_cursor kt_cursor; + char * entry_princ_s = NULL; + int rval = 0; + + krberr = krb5_kt_start_seq_get(context, ktid, &kt_cursor); + memset(&entry, 0, sizeof(entry)); + while (krb5_kt_next_entry(context, ktid, &entry, &kt_cursor) == 0) { + krberr = krb5_unparse_name(context, entry.principal, &entry_princ_s); + if (krberr) { + fprintf(stderr, "Unable to parse principal\n"); + if (debug) { + fprintf(stderr, "krb5_unparse_name %d: %s\n", krberr, error_message(krberr)); + } + rval = 4; + goto done; + } + + if (strstr(entry_princ_s, realm) != NULL) { + rval = remove_principal(context, ktid, entry_princ_s, debug); + if (rval != 0) + goto done; + /* Have to reset the cursor */ + krberr = krb5_kt_start_seq_get(context, ktid, &kt_cursor); + } + } + +done: + + return rval; +} + +int +main(int argc, char **argv) +{ + krb5_context context; + krb5_error_code krberr; + krb5_keytab ktid; + char * ktname; + char * atrealm; + poptContext pc; + static const char *keytab = NULL; + static const char *principal = NULL; + static const char *realm = NULL; + int debug = 0; + int ret, rval; + struct poptOption options[] = { + { "debug", 'd', POPT_ARG_NONE, &debug, 0, "Print debugging information", "Debugging output" }, + { "principal", 'p', POPT_ARG_STRING, &principal, 0, "The principal to get a keytab for (ex: ftp/ftp.example.com@EXAMPLE.COM)", "Kerberos Service Principal Name" }, + { "keytab", 'k', POPT_ARG_STRING, &keytab, 0, "File were to store the keytab information", "Keytab File Name" }, + { "realm", 'r', POPT_ARG_STRING, &realm, 0, "Remove all principals in this realm", "Realm name" }, +{ NULL, 0, POPT_ARG_NONE, NULL, 0, NULL, NULL } + }; + + memset(&ktid, 0, sizeof(ktid)); + + krberr = krb5_init_context(&context); + if (krberr) { + fprintf(stderr, "Kerberos context initialization failed\n"); + exit(1); + } + + pc = poptGetContext("ipa-rmkeytab", argc, (const char **)argv, options, 0); + ret = poptGetNextOpt(pc); + if (ret != -1 || (!principal && !realm) || !keytab) { + poptPrintUsage(pc, stderr, 0); + rval = 1; + goto cleanup; + } + + ret = asprintf(&ktname, "WRFILE:%s", keytab); + if (ret == -1) { + rval = 2; + goto cleanup; + } + + /* The remove_realm function just does a substring match. Ensure that + * the string we pass in looks like a realm. + */ + if (realm) { + if (realm[0] != '@') + ret = asprintf(&atrealm, "@%s", realm); + if (ret == -1) { + rval = 2; + goto cleanup; + } + else + atrealm = strcpy(atrealm, realm); + } + + krberr = krb5_kt_resolve(context, ktname, &ktid); + if (krberr) { + fprintf(stderr, "Failed to open keytab '%s'\n", keytab); + rval = 3; + goto cleanup; + } + + if (principal) + rval = remove_principal(context, ktid, principal, debug); + else if (realm) + rval = remove_realm(context, ktid, atrealm, debug); + +cleanup: + if (rval == 0 || rval > 3) { + krberr = krb5_kt_close(context, ktid); + if (krberr) { + fprintf(stderr, "Closing keytab failed\n"); + if (debug) + fprintf(stderr, "krb5_kt_close %d: %s\n", krberr, error_message(krberr)); + } + } + + krb5_free_context(context); + + poptFreeContext(pc); + + return rval; +} diff --git a/ipa-client/man/Makefile.am b/ipa-client/man/Makefile.am index eee7144c1..bf11e39a8 100644 --- a/ipa-client/man/Makefile.am +++ b/ipa-client/man/Makefile.am @@ -6,6 +6,7 @@ NULL = man_MANS = \ ipa-getkeytab.1 \ + ipa-rmkeytab.1 \ ipa-client-install.1 \ ipa-join.1 diff --git a/ipa-client/man/ipa-rmkeytab.1 b/ipa-client/man/ipa-rmkeytab.1 new file mode 100644 index 000000000..a60f45cf9 --- /dev/null +++ b/ipa-client/man/ipa-rmkeytab.1 @@ -0,0 +1,90 @@ +.\" A man page for ipa-rmkeytab +.\" Copyright (C) 2009 Red Hat, Inc. +.\" +.\" This is free software; you can redistribute it and/or modify it under +.\" the terms of the GNU Library General Public License as published by +.\" the Free Software Foundation; version 2 only +.\" +.\" This program is distributed in the hope that it will be useful, but +.\" WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +.\" General Public License for more details. +.\" +.\" You should have received a copy of the GNU Library General Public +.\" License along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" Author: Rob Crittenden +.\" +.\" +.TH "ipa-rmkeytab" "1" "Oct 30 2009" "freeipa" "" +.SH "NAME" +ipa\-rmkeytab \- Remove a kerberos principal from a keytab +.SH "SYNOPSIS" +ipa\-rmkeytab [ \fB\-p\fR principal\-name ] [ \fB\-k\fR keytab\-file ] [ \fB\-r\fR realm ] [ \fB\-d\fR ] + +.SH "DESCRIPTION" +Removes a kerberos principal from a \fIkeytab\fR. + +Kerberos keytabs are used for services (like sshd) to +perform kerberos authentication. A keytab is a file +with one or more secrets (or keys) for a kerberos +principal. + +A kerberos service principal is a kerberos identity +that can be used for authentication. Service principals +contain the name of the service, the hostname of the +server, and the realm name. + +ipa\-rmkeytab provides two ways to remove principals. +A specific principal can be removed or all principals for all +principals for a given realm can be removed. + +All encryption types and versions of a principal are removed. + +The realm may be included when removing a specific principal but +it is not required. + +\fBNOTE:\fR removing a principal from the keytab does not affect +the Kerberos principal stored in the IPA server. It merely removes +the entry from the local keytab. +.SH "OPTIONS" +.TP +\fB\-p principal\-name\fR +The non\-realm part of the full principal name. +.TP +\fB\-k keytab\-file\fR +The keytab file where to append the new key (will be +created if it does not exist). +.TP +\fB\-r realm\fR +A realm to remove all principals for. +.TP +\fB\-d\fR +Debug mode. Additional information is displayed. +.SH "EXAMPLES" +Remove the NFS service principal on the host foo.example.com from /tmp/nfs.keytab. + + # ipa\-rmkeytab \-p nfs/foo.example.com \-k /tmp/nfs.keytab + +Remove the ldap service principal onthe host foo.example.com from /etc/krb5.keytab. + + # ipa\-rmkeytab \-p ldap/foo.example.com \-k /tmp/ldap.keytab + +Remove all principals for the realm EXAMPLE.COM. + + # ipa\-rmkeytab \-r EXAMPLE.COM \-k /tmp/ldap.keytab +.SH "EXIT STATUS" +The exit status is 0 on success, nonzero on error. + +1 Kerberos initialization failed + +2 Memory allocation error + +3 Unable to open keytab + +4 Unable to parse the principal name + +5 Principal name not found in keytab + +6 Unable to remove principal from keytab diff --git a/ipa.spec.in b/ipa.spec.in index 76f9e57c1..84884a5cd 100644 --- a/ipa.spec.in +++ b/ipa.spec.in @@ -425,6 +425,7 @@ fi %doc LICENSE README %{_sbindir}/ipa-client-install %{_sbindir}/ipa-getkeytab +%{_sbindir}/ipa-rmkeytab %{_sbindir}/ipa-join %dir %{_usr}/share/ipa %dir %{_usr}/share/ipa/ipaclient @@ -435,6 +436,7 @@ fi %dir %{python_sitelib}/ipaclient %{python_sitelib}/ipaclient/*.py* %{_mandir}/man1/ipa-getkeytab.1.gz +%{_mandir}/man1/ipa-rmkeytab.1.gz %{_mandir}/man1/ipa-client-install.1.gz %{_mandir}/man1/ipa-join.1.gz @@ -482,6 +484,9 @@ fi %endif %changelog +* Fri Dec 4 2009 Rob Crittenden - 1.99-13 +- Add ipa-rmkeytab tool + * Tue Dec 1 2009 Rob Crittenden - 1.99-12 - Set minimum of python-pyasn1 to 0.0.9a so we have support for the ASN.1 Any type -- cgit