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 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 c, *vprincstr, *ktname, *token, *lasts, *newpw; 61 int 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 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 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 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