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
main(int argc,char ** argv)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
kt_remove_entries(krb5_context ctx,krb5_keytab kt,const krb5_principal princ)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
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)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
usage()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