This makes it clearer what the return value means. Replace closing of keytab based on the numeric return value and do it based on whether the keytab was opened at all. https://pagure.io/freeipa/issue/8658 Signed-off-by: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
276 lines
8.1 KiB
C
276 lines
8.1 KiB
C
/* Authors: Rob Crittenden <rcritten@redhat.com>
|
|
*
|
|
* 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, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <krb5.h>
|
|
#include <popt.h>
|
|
#include <errno.h>
|
|
|
|
#include "ipa-client-common.h"
|
|
#include "config.h"
|
|
|
|
#define KERBEROS_ERROR 1
|
|
#define OOM_ERROR 2
|
|
#define KEYTAB_ERROR 3
|
|
#define PRINCIPAL_ERROR 4
|
|
#define NOT_FOUND 5
|
|
#define REMOVE_ERROR 6
|
|
|
|
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 %1$d: %2$s\n"),
|
|
krberr, error_message(krberr));
|
|
rval = PRINCIPAL_ERROR;
|
|
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 = KEYTAB_ERROR;
|
|
goto done;
|
|
}
|
|
fprintf(stderr, _("principal not found\n"));
|
|
if (debug)
|
|
fprintf(stderr, _("krb5_kt_get_entry %1$d: %2$s\n"),
|
|
krberr, error_message(krberr));
|
|
rval = NOT_FOUND;
|
|
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 %1$d: %2$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;
|
|
bool realm_found = false;
|
|
|
|
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 %1$d: %2$s\n"),
|
|
krberr, error_message(krberr));
|
|
}
|
|
rval = PRINCIPAL_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
/* keytab entries are locked when looping. Temporarily suspend
|
|
* the looping. */
|
|
krb5_kt_end_seq_get(context, ktid, &kt_cursor);
|
|
|
|
if (strstr(entry_princ_s, realm) != NULL) {
|
|
realm_found = true;
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (!realm_found) {
|
|
fprintf(stderr, _("realm not found\n"));
|
|
return 5;
|
|
}
|
|
|
|
done:
|
|
|
|
return rval;
|
|
}
|
|
|
|
int
|
|
main(int argc, const char **argv)
|
|
{
|
|
krb5_context context;
|
|
krb5_error_code krberr;
|
|
krb5_keytab ktid;
|
|
krb5_kt_cursor cursor;
|
|
char * ktname = NULL;
|
|
char * atrealm = NULL;
|
|
poptContext pc;
|
|
static const char *keytab = NULL;
|
|
static const char *principal = NULL;
|
|
static const char *realm = NULL;
|
|
int debug = 0;
|
|
int ret, rval = 0;
|
|
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 remove from the keytab (ex: ftp/ftp.example.com@EXAMPLE.COM)"),
|
|
_("Kerberos Service Principal Name") },
|
|
{ "keytab", 'k', POPT_ARG_STRING, &keytab, 0,
|
|
_("The keytab file to remove the principcal(s) from"), _("Keytab File Name") },
|
|
{ "realm", 'r', POPT_ARG_STRING, &realm, 0,
|
|
_("Remove all principals in this realm"), _("Realm name") },
|
|
POPT_AUTOHELP
|
|
POPT_TABLEEND
|
|
};
|
|
|
|
ret = init_gettext();
|
|
if (ret) {
|
|
fprintf(stderr, "Failed to load translations\n");
|
|
}
|
|
|
|
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 = KERBEROS_ERROR;
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = asprintf(&ktname, "WRFILE:%s", keytab);
|
|
if (ret == -1) {
|
|
rval = OOM_ERROR;
|
|
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 = OOM_ERROR;
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
atrealm = strdup(realm);
|
|
|
|
if (NULL == atrealm) {
|
|
rval = OOM_ERROR;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
krberr = krb5_kt_resolve(context, ktname, &ktid);
|
|
if (krberr) {
|
|
fprintf(stderr, _("Failed to open keytab '%1$s': %2$s\n"), keytab,
|
|
error_message(krberr));
|
|
rval = KEYTAB_ERROR;
|
|
goto cleanup;
|
|
}
|
|
krberr = krb5_kt_start_seq_get(context, ktid, &cursor);
|
|
if (krberr) {
|
|
fprintf(stderr, _("Failed to open keytab '%1$s': %2$s\n"), keytab,
|
|
error_message(krberr));
|
|
rval = KEYTAB_ERROR;
|
|
goto cleanup;
|
|
}
|
|
krb5_kt_end_seq_get(context, ktid, &cursor);
|
|
|
|
if (principal)
|
|
rval = remove_principal(context, ktid, principal, debug);
|
|
else if (realm)
|
|
rval = remove_realm(context, ktid, atrealm, debug);
|
|
|
|
cleanup:
|
|
if (ktid) {
|
|
krberr = krb5_kt_close(context, ktid);
|
|
if (krberr) {
|
|
fprintf(stderr, _("Closing keytab failed\n"));
|
|
if (debug)
|
|
fprintf(stderr, _("krb5_kt_close %1$d: %2$s\n"),
|
|
krberr, error_message(krberr));
|
|
}
|
|
}
|
|
|
|
krb5_free_context(context);
|
|
|
|
poptFreeContext(pc);
|
|
|
|
free(atrealm);
|
|
free(ktname);
|
|
|
|
return rval;
|
|
}
|