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