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] [-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 }