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