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 }