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