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