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