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 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 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 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 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