keytab.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
*
* $Id: keytab.c,v 1.26 2000/02/19 01:57:07 tlyu Exp $
* $Source: /cvs/krbdev/krb5/src/kadmin/cli/keytab.c,v $
*/
/*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#if !defined(lint) && !defined(__CODECENTER__)
static char *rcsid = "$Header: /cvs/krbdev/krb5/src/kadmin/cli/keytab.c,v 1.26 2000/02/19 01:57:07 tlyu Exp $";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <krb5.h>
#include <k5-int.h>
#include <kadm5/admin.h>
static int add_principal(void *handle, char *keytab_str, krb5_keytab keytab,
int keepold,
int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
char *princ_str);
static int remove_principal(char *keytab_str, krb5_keytab keytab, char
*princ_str, char *kvno_str);
static char *etype_string(krb5_enctype enctype);
extern char *krb5_defkeyname;
extern char *whoami;
extern krb5_context context;
extern void *handle;
static int quiet;
void
add_usage()
{
fprintf(stderr, "%s: %s\n", gettext("Usage"),
"ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
"[principal | -glob princ-exp] [...]\n");
}
void
rem_usage()
{
fprintf(stderr, "%s: %s\n",
gettext("Usage"),
"ktremove [-k[eytab] keytab] [-q] principal "
"[kvno|\"all\"|\"old\"]\n");
}
int
process_keytab(krb5_context context, char **keytab_str,
krb5_keytab *keytab)
{
int code;
char buf[BUFSIZ];
if (*keytab_str == NULL) {
if (code = krb5_kt_default(context, keytab)) {
com_err(whoami, code, gettext("while opening default keytab"));
return (1);
}
if (code = krb5_kt_get_name(context, *keytab, buf, BUFSIZ)) {
com_err(whoami, code, gettext("while retrieving keytab name"));
return (1);
}
if (!(*keytab_str = strdup(buf))) {
com_err(whoami, ENOMEM, gettext("while creating keytab name"));
return(1);
}
} else {
if (strchr(*keytab_str, ':') != NULL) {
*keytab_str = strdup(*keytab_str);
if (*keytab_str == NULL) {
com_err(whoami, ENOMEM,
gettext("while creating keytab name"));
return (1);
}
} else {
char *tmp = *keytab_str;
*keytab_str = (char *)
malloc(strlen("WRFILE:")+strlen(tmp)+1);
if (*keytab_str == NULL) {
com_err(whoami, ENOMEM,
gettext("while creating keytab name"));
return (1);
}
sprintf(*keytab_str, "WRFILE:%s", tmp);
}
code = krb5_kt_resolve(context, *keytab_str, keytab);
if (code != 0) {
com_err(whoami, code,
gettext("while resolving keytab %s"), *keytab_str);
free(keytab_str);
return (1);
}
}
return (0);
}
void
kadmin_keytab_add(int argc, char **argv)
{
krb5_keytab keytab = 0;
char *princ_str, *keytab_str = NULL, **princs;
int code, num, i;
krb5_error_code retval;
int keepold = 0, n_ks_tuple = 0;
krb5_key_salt_tuple *ks_tuple = NULL;
argc--;
argv++;
quiet = 0;
while (argc) {
if (strncmp(*argv, "-k", 2) == 0) {
argc--;
argv++;
if (!argc || keytab_str) {
add_usage();
return;
}
keytab_str = *argv;
} else if (strcmp(*argv, "-q") == 0) {
quiet++;
} else if (strcmp(*argv, "-e") == 0) {
argc--;
if (argc < 1) {
add_usage();
return;
}
retval = krb5_string_to_keysalts(*++argv, ", \t", ":.-", 0,
&ks_tuple, &n_ks_tuple);
if (retval) {
com_err("ktadd", retval,
gettext("while parsing keysalts %s"),
*argv);
return;
}
} else
break;
argc--;
argv++;
}
if (argc == 0) {
add_usage();
return;
}
if (process_keytab(context, &keytab_str, &keytab))
return;
while (*argv) {
if (strcmp(*argv, "-glob") == 0) {
if (*++argv == NULL) {
add_usage();
break;
}
if (code = kadm5_get_principals(handle, *argv,
&princs, &num)) {
com_err(whoami, code,
gettext("while expanding expression "
"\"%s\"."),
*argv);
argv++;
continue;
}
for (i = 0; i < num; i++)
(void) add_principal(handle, keytab_str, keytab,
keepold, n_ks_tuple, ks_tuple,
princs[i]);
kadm5_free_name_list(handle, princs, num);
} else
(void) add_principal(handle, keytab_str, keytab,
keepold, n_ks_tuple, ks_tuple,
*argv);
argv++;
}
code = krb5_kt_close(context, keytab);
if (code != 0)
com_err(whoami, code, gettext("while closing keytab"));
free(keytab_str);
}
void
kadmin_keytab_remove(int argc, char **argv)
{
krb5_keytab keytab = 0;
char *princ_str, *keytab_str = NULL;
int code;
argc--;
argv++;
quiet = 0;
while (argc) {
if (strncmp(*argv, "-k", 2) == 0) {
argc--;
argv++;
if (!argc || keytab_str) {
rem_usage();
return;
}
keytab_str = *argv;
} else if (strcmp(*argv, "-q") == 0) {
quiet++;
} else
break;
argc--;
argv++;
}
if (argc != 1 && argc != 2) {
rem_usage();
return;
}
if (process_keytab(context, &keytab_str, &keytab))
return;
(void) remove_principal(keytab_str, keytab, argv[0], argv[1]);
code = krb5_kt_close(context, keytab);
if (code != 0)
com_err(whoami, code, gettext("while closing keytab"));
free(keytab_str);
}
int add_principal(void *handle, char *keytab_str, krb5_keytab keytab,
int keepold, int n_ks_tuple,
krb5_key_salt_tuple *ks_tuple,
char *princ_str)
{
kadm5_principal_ent_rec princ_rec;
krb5_principal princ;
krb5_keytab_entry new_entry;
krb5_keyblock *keys;
int code, code2, mask, nkeys, i;
int nktypes = 0;
krb5_key_salt_tuple *permitted_etypes = NULL;
(void) memset((char *)&princ_rec, 0, sizeof(princ_rec));
princ = NULL;
keys = NULL;
nkeys = 0;
code = krb5_parse_name(context, princ_str, &princ);
if (code != 0) {
com_err(whoami, code,
gettext("while parsing -add principal name %s"),
princ_str);
goto cleanup;
}
if (ks_tuple == NULL) {
krb5_enctype *ptr, *ktypes = NULL;
code = krb5_get_permitted_enctypes(context, &ktypes);
if (!code && ktypes && *ktypes) {
krb5_int32 salttype;
/*
* Count the results. This is stupid, the API above
* should have included an output param to indicate
* the size of the list that is returned.
*/
for (ptr = ktypes; *ptr; ptr++) nktypes++;
/* Allocate a new key-salt tuple set */
permitted_etypes = (krb5_key_salt_tuple *)malloc (
sizeof (krb5_key_salt_tuple) * nktypes);
if (permitted_etypes == NULL) {
free(ktypes);
return (ENOMEM);
}
/*
* Because the keysalt parameter doesn't matter for
* keys stored in the keytab, use the default "normal"
* salt for all keys
*/
(void) krb5_string_to_salttype("normal", &salttype);
for (i = 0; i < nktypes; i++) {
permitted_etypes[i].ks_enctype = ktypes[i];
permitted_etypes[i].ks_salttype = salttype;
}
free(ktypes);
} else {
if (ktypes)
free(ktypes);
goto cleanup;
}
} else {
permitted_etypes = ks_tuple;
nktypes = n_ks_tuple;
}
code = kadm5_randkey_principal_3(handle, princ,
keepold, nktypes, permitted_etypes,
&keys, &nkeys);
#ifndef _KADMIN_LOCAL_
/* this block is not needed in the kadmin.local client */
/*
* If the above call failed, we may be talking to an older
* admin server, so try the older API.
*/
if (code == KADM5_RPC_ERROR) {
code = kadm5_randkey_principal_old(handle, princ, &keys, &nkeys);
}
#endif /* !KADMIN_LOCAL */
if (code != 0) {
if (code == KADM5_UNK_PRINC) {
fprintf(stderr,
gettext("%s: Principal %s does not exist.\n"),
whoami, princ_str);
} else {
com_err(whoami, code,
gettext("while changing %s's key"),
princ_str);
}
goto cleanup;
}
code = kadm5_get_principal(handle, princ, &princ_rec,
KADM5_PRINCIPAL_NORMAL_MASK);
if (code != 0) {
com_err(whoami, code, gettext("while retrieving principal"));
goto cleanup;
}
for (i = 0; i < nkeys; i++) {
memset((char *) &new_entry, 0, sizeof(new_entry));
new_entry.principal = princ;
new_entry.key = keys[i];
new_entry.vno = princ_rec.kvno;
code = krb5_kt_add_entry(context, keytab, &new_entry);
if (code != 0) {
com_err(whoami, code,
gettext("while adding key to keytab"));
(void) kadm5_free_principal_ent(handle, &princ_rec);
goto cleanup;
}
if (!quiet)
printf(gettext("Entry for principal %s with kvno %d, "
"encryption type %s added to keytab %s.\n"),
princ_str, princ_rec.kvno,
etype_string(keys[i].enctype), keytab_str);
}
code = kadm5_free_principal_ent(handle, &princ_rec);
if (code != 0) {
com_err(whoami, code, gettext("while freeing principal entry"));
goto cleanup;
}
cleanup:
if (nkeys) {
for (i = 0; i < nkeys; i++)
krb5_free_keyblock_contents(context, &keys[i]);
free(keys);
}
if (princ)
krb5_free_principal(context, princ);
if (permitted_etypes != NULL && ks_tuple == NULL)
free(permitted_etypes);
return (code);
}
int
remove_principal(char *keytab_str, krb5_keytab keytab, char
*princ_str, char *kvno_str)
{
krb5_principal princ;
krb5_keytab_entry entry;
krb5_kt_cursor cursor;
enum {
UNDEF, SPEC, HIGH, ALL, OLD
} mode;
int code, kvno, did_something;
code = krb5_parse_name(context, princ_str, &princ);
if (code != 0) {
com_err(whoami, code,
gettext("while parsing principal name %s"),
princ_str);
return (code);
}
mode = UNDEF;
if (kvno_str == NULL) {
mode = HIGH;
kvno = 0;
} else if (strcmp(kvno_str, "all") == 0) {
mode = ALL;
kvno = 0;
} else if (strcmp(kvno_str, "old") == 0) {
mode = OLD;
kvno = 0;
} else {
mode = SPEC;
kvno = atoi(kvno_str);
}
/* kvno is set to specified value for SPEC, 0 otherwise */
code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry);
if (code != 0) {
if (code == ENOENT) {
fprintf(stderr,
gettext("%s: Keytab %s does not exist.\n"),
whoami, keytab_str);
} else if (code == KRB5_KT_NOTFOUND) {
if (mode != SPEC)
fprintf(stderr,
gettext("%s: No entry for principal "
"%s exists in keytab %s\n"),
whoami, princ_str, keytab_str);
else
fprintf(stderr,
gettext("%s: No entry for principal "
"%s with kvno %d exists in "
"keytab %s.\n"),
whoami, princ_str, kvno, keytab_str);
} else {
com_err(whoami, code,
gettext("while retrieving highest "
"kvno from keytab"));
}
return (code);
}
/* set kvno to spec'ed value for SPEC, highest kvno otherwise */
kvno = entry.vno;
krb5_kt_free_entry(context, &entry);
code = krb5_kt_start_seq_get(context, keytab, &cursor);
if (code != 0) {
com_err(whoami, code, gettext("while starting keytab scan"));
return (code);
}
did_something = 0;
while ((code = krb5_kt_next_entry(context,
keytab, &entry, &cursor)) == 0) {
if (krb5_principal_compare(context, princ, entry.principal) &&
((mode == ALL) ||
(mode == SPEC && entry.vno == kvno) ||
(mode == OLD && entry.vno != kvno) ||
(mode == HIGH && entry.vno == kvno))) {
/*
* Ack! What a kludge... the scanning functions
* lock the keytab so entries cannot be removed
* while they are operating.
*/
code = krb5_kt_end_seq_get(context, keytab, &cursor);
if (code != 0) {
com_err(whoami, code,
gettext("while temporarily "
"ending keytab scan"));
return (code);
}
code = krb5_kt_remove_entry(context, keytab, &entry);
if (code != 0) {
com_err(whoami, code,
gettext("while deleting entry "
"from keytab"));
return (code);
}
code = krb5_kt_start_seq_get(context, keytab, &cursor);
if (code != 0) {
com_err(whoami, code,
gettext("while restarting keytab scan"));
return (code);
}
did_something++;
if (!quiet)
printf(gettext("Entry for principal "
"%s with kvno %d "
"removed from keytab %s.\n"),
princ_str, entry.vno, keytab_str);
}
krb5_kt_free_entry(context, &entry);
}
if (code && code != KRB5_KT_END) {
com_err(whoami, code, gettext("while scanning keytab"));
return (code);
}
if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) {
com_err(whoami, code, gettext("while ending keytab scan"));
return (code);
}
/*
* If !did_someting then mode must be OLD or we would have already
* returned with an error. But check it anyway just to prevent
* unexpected error messages...
*/
if (!did_something && mode == OLD) {
fprintf(stderr,
gettext("%s: There is only one entry for principal "
"%s in keytab %s\n"),
whoami, princ_str, keytab_str);
return (1);
}
return (0);
}
/*
* etype_string(enctype): return a string representation of the
* encryption type. XXX copied from klist.c; this should be a
* library function, or perhaps just #defines
*/
static char *
etype_string(enctype)
krb5_enctype enctype;
{
static char buf[100];
krb5_error_code ret;
if (ret = krb5_enctype_to_string(enctype, buf, sizeof(buf)))
sprintf(buf, "etype %d", enctype);
return (buf);
}