Home | History | Annotate | Download | only in kclient
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <strings.h>
     30 #include <locale.h>
     31 #include <netdb.h>
     32 #include "k5-int.h"
     33 
     34 #define	QUOTE(x)	#x
     35 #define	VAL2STR(x)	QUOTE(x)
     36 
     37 static char *whoami = NULL;
     38 
     39 static void kt_add_entry(krb5_context ctx, krb5_keytab kt,
     40 	const krb5_principal princ, const krb5_principal sprinc,
     41 	krb5_enctype enctype, krb5_kvno kvno, const char *pw);
     42 
     43 static krb5_error_code kt_remove_entries(krb5_context ctx, krb5_keytab kt,
     44 	const krb5_principal princ);
     45 
     46 static void usage();
     47 
     48 int
     49 main(int argc, char **argv)
     50 {
     51 	krb5_context ctx = NULL;
     52 	krb5_error_code code = 0;
     53 	krb5_enctype *enctypes;
     54 	int enctype_count = 0;
     55 	krb5_ccache cc = NULL;
     56 	krb5_keytab kt = NULL;
     57 	krb5_kvno kvno = 1;
     58 	krb5_principal victim, salt;
     59 	char c, *vprincstr, *ktname, *token, *lasts, *newpw;
     60 	int result_code, i, len, nflag = 0;
     61 	krb5_data result_code_string, result_string;
     62 
     63 	(void) setlocale(LC_ALL, "");
     64 
     65 #if !defined(TEXT_DOMAIN)
     66 #define	TEXT_DOMAIN "SYS_TEST"
     67 #endif /* TEXT_DOMAIN */
     68 
     69 	(void) textdomain(TEXT_DOMAIN);
     70 
     71 	/* Misc init stuff */
     72 	(void) memset(&result_code_string, 0, sizeof (result_code_string));
     73 	(void) memset(&result_string, 0, sizeof (result_string));
     74 
     75 	whoami = argv[0];
     76 
     77 	code = krb5_init_context(&ctx);
     78 	if (code != 0) {
     79 		com_err(whoami, code, gettext("krb5_init_context() failed"));
     80 		exit(1);
     81 	}
     82 
     83 	while ((c = getopt(argc, argv, "v:c:k:e:ns:")) != -1) {
     84 		switch (c) {
     85 		case 'n':
     86 			nflag++;
     87 			break;
     88 		case 'k':
     89 			if (kt != NULL)
     90 				usage();
     91 			len = snprintf(NULL, 0, "WRFILE:%s", optarg) + 1;
     92 			if ((ktname = malloc(len)) == NULL) {
     93 				(void) fprintf(stderr,
     94 				    gettext("Couldn't allocate memory\n"));
     95 				exit(1);
     96 			}
     97 			(void) snprintf(ktname, len, "WRFILE:%s", optarg);
     98 			if ((code = krb5_kt_resolve(ctx, ktname, &kt)) != 0) {
     99 				com_err(whoami, code,
    100 				    gettext("Couldn't open/create "
    101 				    "keytab %s"), optarg);
    102 				exit(1);
    103 			}
    104 			break;
    105 		case 'c':
    106 			if (cc != NULL)
    107 				usage();
    108 			if ((code = krb5_cc_resolve(ctx, optarg, &cc)) != 0) {
    109 				com_err(whoami, code,
    110 				    gettext("Couldn't open ccache %s"), optarg);
    111 				exit(1);
    112 			}
    113 			break;
    114 		case 'e':
    115 			len = strlen(optarg);
    116 			token = strtok_r(optarg, ",\t,", &lasts);
    117 
    118 			if (token == NULL)
    119 				usage();
    120 
    121 			do {
    122 				if (enctype_count++ == 0) {
    123 					enctypes = malloc(sizeof (*enctypes));
    124 				} else {
    125 					enctypes = realloc(enctypes,
    126 					    sizeof (*enctypes) * enctype_count);
    127 				}
    128 				if (enctypes == NULL) {
    129 					(void) fprintf(stderr, gettext
    130 					    ("Couldn't allocate memory"));
    131 					exit(1);
    132 				}
    133 				code = krb5_string_to_enctype(token,
    134 				    &enctypes[enctype_count - 1]);
    135 
    136 				if (code != 0) {
    137 					com_err(whoami, code, gettext("Unknown "
    138 					    "or unsupported enctype %s"),
    139 					    optarg);
    140 					exit(1);
    141 				}
    142 			} while ((token = strtok_r(NULL, ",\t ", &lasts)) !=
    143 			    NULL);
    144 			break;
    145 		case 'v':
    146 			kvno = (krb5_kvno) atoi(optarg);
    147 			break;
    148 		case 's':
    149 			vprincstr = optarg;
    150 			code = krb5_parse_name(ctx, vprincstr, &salt);
    151 			if (code != 0) {
    152 				com_err(whoami, code,
    153 				    gettext("krb5_parse_name(%s) failed"),
    154 				    vprincstr);
    155 				exit(1);
    156 			}
    157 			break;
    158 		default:
    159 			usage();
    160 			break;
    161 		}
    162 	}
    163 
    164 	if (nflag && enctype_count == 0)
    165 		usage();
    166 
    167 	if (nflag == 0 && cc == NULL &&
    168 	    (code = krb5_cc_default(ctx, &cc)) != 0) {
    169 		com_err(whoami, code, gettext("Could not find a ccache"));
    170 		exit(1);
    171 	}
    172 
    173 	if (enctype_count > 0 && kt == NULL &&
    174 	    (code = krb5_kt_default(ctx, &kt)) != 0) {
    175 		com_err(whoami, code, gettext("No keytab specified"));
    176 		exit(1);
    177 	}
    178 
    179 	if (argc != (optind + 1))
    180 		usage();
    181 
    182 	vprincstr = argv[optind];
    183 	code = krb5_parse_name(ctx, vprincstr, &victim);
    184 	if (code != 0) {
    185 		com_err(whoami, code, gettext("krb5_parse_name(%s) failed"),
    186 		    vprincstr);
    187 		exit(1);
    188 	}
    189 
    190 	if (!isatty(fileno(stdin))) {
    191 		char buf[PASS_MAX + 1];
    192 
    193 		if (scanf("%" VAL2STR(PASS_MAX) "s", &buf) != 1) {
    194 			(void) fprintf(stderr,
    195 			    gettext("Couldn't read new password\n"));
    196 			exit(1);
    197 		}
    198 
    199 		newpw = strdup(buf);
    200 		if (newpw == NULL) {
    201 			(void) fprintf(stderr,
    202 			    gettext("Couldn't allocate memory\n"));
    203 			exit(1);
    204 		}
    205 	} else {
    206 		newpw = getpassphrase(gettext("Enter new password: "));
    207 		if (newpw == NULL) {
    208 			(void) fprintf(stderr,
    209 			    gettext("Couldn't read new password\n"));
    210 			exit(1);
    211 		}
    212 
    213 		newpw = strdup(newpw);
    214 		if (newpw == NULL) {
    215 			(void) fprintf(stderr,
    216 			    gettext("Couldn't allocate memory\n"));
    217 			exit(1);
    218 		}
    219 	}
    220 
    221 	if (nflag == 0) {
    222 		code = krb5_set_password_using_ccache(ctx, cc, newpw, victim,
    223 		    &result_code, &result_code_string, &result_string);
    224 		if (code != 0) {
    225 			com_err(whoami, code,
    226 			    gettext("krb5_set_password() failed"));
    227 			exit(1);
    228 		}
    229 		krb5_cc_close(ctx, cc);
    230 
    231 		(void) printf("Result: %.*s (%d) %.*s\n",
    232 		    result_code == 0 ?
    233 		    strlen("success") : result_code_string.length,
    234 		    result_code == 0 ? "success" : result_code_string.data,
    235 		    result_code,
    236 		    result_string.length, result_string.data);
    237 
    238 		if (result_code != 0) {
    239 			(void) fprintf(stderr, gettext("Exiting...\n"));
    240 			exit(result_code);
    241 		}
    242 	}
    243 
    244 	if (enctype_count && (code = kt_remove_entries(ctx, kt, victim)))
    245 		goto error;
    246 
    247 	for (i = 0; i < enctype_count; i++)
    248 		kt_add_entry(ctx, kt, victim, salt, enctypes[i], kvno, newpw);
    249 
    250 error:
    251 	if (kt != NULL)
    252 		krb5_kt_close(ctx, kt);
    253 
    254 	return (code ? 1 : 0);
    255 }
    256 
    257 static
    258 krb5_error_code
    259 kt_remove_entries(krb5_context ctx, krb5_keytab kt, const krb5_principal princ)
    260 {
    261 	krb5_error_code code;
    262 	krb5_kt_cursor cursor;
    263 	krb5_keytab_entry entry;
    264 
    265 	/*
    266 	 * This is not a fatal error, we expect this to fail in the majority
    267 	 * of cases (when clients are first initialized).
    268 	 */
    269 	code = krb5_kt_get_entry(ctx, kt, princ, 0, 0, &entry);
    270 	if (code != 0) {
    271 		com_err(whoami, code,
    272 		    gettext("Could not retrieve entry in keytab"));
    273 		return (0);
    274 	}
    275 
    276 	krb5_kt_free_entry(ctx, &entry);
    277 
    278 	code = krb5_kt_start_seq_get(ctx, kt, &cursor);
    279 	if (code != 0) {
    280 		com_err(whoami, code, gettext("While starting keytab scan"));
    281 		return (code);
    282 	}
    283 
    284 	while ((code = krb5_kt_next_entry(ctx, kt, &entry, &cursor)) == 0) {
    285 		if (krb5_principal_compare(ctx, princ, entry.principal)) {
    286 
    287 			code = krb5_kt_end_seq_get(ctx, kt, &cursor);
    288 			if (code != 0) {
    289 				com_err(whoami, code,
    290 				    gettext("While temporarily "
    291 				    "ending keytab scan"));
    292 				return (code);
    293 			}
    294 
    295 			code = krb5_kt_remove_entry(ctx, kt, &entry);
    296 			if (code != 0) {
    297 				com_err(whoami, code,
    298 				    gettext("While deleting entry "
    299 				    "from keytab"));
    300 				return (code);
    301 			}
    302 
    303 			code = krb5_kt_start_seq_get(ctx, kt, &cursor);
    304 			if (code != 0) {
    305 				com_err(whoami, code,
    306 				    gettext("While restarting keytab scan"));
    307 				return (code);
    308 			}
    309 		}
    310 
    311 		krb5_kt_free_entry(ctx, &entry);
    312 	}
    313 
    314 	if (code && code != KRB5_KT_END) {
    315 		com_err(whoami, code, gettext("While scanning keytab"));
    316 		return (code);
    317 	}
    318 
    319 	if ((code = krb5_kt_end_seq_get(ctx, kt, &cursor))) {
    320 		com_err(whoami, code, gettext("While ending keytab scan"));
    321 		return (code);
    322 	}
    323 
    324 	return (0);
    325 }
    326 
    327 static
    328 void
    329 kt_add_entry(krb5_context ctx, krb5_keytab kt, const krb5_principal princ,
    330 	const krb5_principal sprinc, krb5_enctype enctype, krb5_kvno kvno,
    331 	const char *pw)
    332 {
    333 	krb5_keytab_entry *entry;
    334 	krb5_data password, salt;
    335 	krb5_keyblock key;
    336 	krb5_error_code code;
    337 	char buf[100];
    338 
    339 	if ((code = krb5_enctype_to_string(enctype, buf, sizeof (buf)))) {
    340 		com_err(whoami, code, gettext("Enctype %d has no name!"),
    341 		    enctype);
    342 		return;
    343 	}
    344 	if ((entry = (krb5_keytab_entry *) malloc(sizeof (*entry))) == NULL) {
    345 		(void) fprintf(stderr, gettext("Couldn't allocate memory"));
    346 		return;
    347 	}
    348 
    349 	(void) memset((char *)entry, 0, sizeof (*entry));
    350 
    351 	password.length = strlen(pw);
    352 	password.data = (char *)pw;
    353 
    354 	if ((code = krb5_principal2salt(ctx, sprinc, &salt)) != 0) {
    355 		com_err(whoami, code,
    356 		    gettext("Could not compute salt for %s"), enctype);
    357 		return;
    358 	}
    359 
    360 	code = krb5_c_string_to_key(ctx, enctype, &password, &salt, &key);
    361 
    362 	if (code != 0) {
    363 		com_err(whoami, code, gettext("Could not compute salt for %s"),
    364 		    enctype);
    365 		krb5_xfree(salt.data);
    366 		return;
    367 	}
    368 
    369 	(void) memcpy(&entry->key, &key, sizeof (krb5_keyblock));
    370 	entry->vno = kvno;
    371 	entry->principal = princ;
    372 
    373 	if ((code = krb5_kt_add_entry(ctx, kt, entry)) != 0) {
    374 		com_err(whoami, code,
    375 		    gettext("Could not add entry to keytab"));
    376 	}
    377 }
    378 
    379 static
    380 void
    381 usage()
    382 {
    383 	(void) fprintf(stderr, gettext("Usage: %s [-c ccache] [-k keytab] "
    384 	    "[-e enctype_list] [-s salt_name] [-n] princ\n"), whoami);
    385 	(void) fprintf(stderr,
    386 	    gettext("\t-n\tDon't set the principal's password\n"));
    387 	(void) fprintf(stderr, gettext("\tenctype_list is a comma or whitespace"
    388 	    " separated list\n"));
    389 	(void) fprintf(stderr, gettext("\tIf -n is used then -k and -e must be "
    390 	    "used\n"));
    391 
    392 	exit(1);
    393 }
    394