Home | History | Annotate | Line # | Download | only in passwd
      1 /* $NetBSD: krb5_passwd.c,v 1.20 2012/04/22 23:43:51 christos Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2000, 2005 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Johan Danielsson; and by Jason R. Thorpe.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  *
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /* uses the `Kerberos Change Password Protocol' */
     34 
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <string.h>
     38 #include <err.h>
     39 #include <errno.h>
     40 #include <pwd.h>
     41 #include <unistd.h>
     42 
     43 #include <openssl/ui.h>
     44 #include <krb5.h>
     45 
     46 #include "extern.h"
     47 
     48 static void
     49 pwkrb5_warn(const char *msg, krb5_context context, krb5_error_code ret)
     50 {
     51     const char *errtxt = krb5_get_error_message(context, ret);
     52     if (errtxt != NULL) {
     53 	    warnx("%s: %s", msg, errtxt);
     54 	    krb5_free_error_message(context, errtxt);
     55     } else
     56 	    warnx("%s: %d", msg, ret);
     57 }
     58 
     59 #ifdef USE_PAM
     60 
     61 void
     62 pwkrb5_usage(const char *prefix)
     63 {
     64 
     65 	(void) fprintf(stderr, "%s %s [-d krb5 | -k] [principal]\n",
     66 	    prefix, getprogname());
     67 }
     68 
     69 void
     70 pwkrb5_argv0_usage(const char *prefix)
     71 {
     72 
     73 	(void) fprintf(stderr, "%s %s [principal]\n",
     74 	    prefix, getprogname());
     75 }
     76 
     77 void
     78 pwkrb5_process(const char *username, int argc, char **argv)
     79 {
     80 	krb5_context context;
     81 	krb5_error_code ret;
     82 	krb5_get_init_creds_opt *opt;
     83 	krb5_principal principal;
     84 	krb5_creds cred;
     85 	int result_code;
     86 	krb5_data result_code_string, result_string;
     87 	char pwbuf[BUFSIZ];
     88 	int ch;
     89 
     90 	while ((ch = getopt(argc, argv, "5ku:")) != -1) {
     91 		switch (ch) {
     92 		case '5':
     93 			/*
     94 			 * Compatibility option that historically
     95 			 * specified to use Kerberos 5.  Silently
     96 			 * ignore it.
     97 			 */
     98 			break;
     99 
    100 		case 'k':
    101 			/*
    102 			 * Absorb the -k that may have gotten us here.
    103 			 */
    104 			break;
    105 
    106 		case 'u':
    107 			/*
    108 			 * Historical option to specify principal.
    109 			 */
    110 			username = optarg;
    111 			break;
    112 
    113 		default:
    114 			usage();
    115 			/* NOTREACHED */
    116 		}
    117 	}
    118 
    119 	argc -= optind;
    120 	argv += optind;
    121 
    122 	switch (argc) {
    123 	case 0:
    124 		/* username already provided */
    125 		break;
    126 	case 1:
    127 		/* overrides -u <principal> */
    128 		username = argv[0];
    129 		break;
    130 	default:
    131 		usage();
    132 		/* NOTREACHED */
    133 	}
    134 
    135 	ret = krb5_init_context(&context);
    136 	if (ret != 0) {
    137 		if (ret == ENXIO)
    138 			errx(1, "Kerberos 5 not in use.");
    139 		errx(1, "Unable to initialize Kerberos 5: %s", strerror(ret));
    140 	}
    141 
    142 	ret = krb5_get_init_creds_opt_alloc(context, &opt);
    143 	if (ret) {
    144 		pwkrb5_warn("failed to allocate opts", context, ret);
    145 		goto bad;
    146 	}
    147 
    148 	krb5_get_init_creds_opt_set_tkt_life(opt, 300L);
    149 	krb5_get_init_creds_opt_set_forwardable(opt, FALSE);
    150 	krb5_get_init_creds_opt_set_proxiable(opt, FALSE);
    151 
    152 	ret = krb5_parse_name(context, username, &principal);
    153 	if (ret) {
    154 		krb5_get_init_creds_opt_free(context, opt);
    155 		pwkrb5_warn("failed to parse principal", context, ret);
    156 		goto bad;
    157 	}
    158 
    159 	ret = krb5_get_init_creds_password(context,
    160 					   &cred,
    161 					   principal,
    162 					   NULL,
    163 					   krb5_prompter_posix,
    164 					   NULL,
    165 					   0L,
    166 					   "kadmin/changepw",
    167 					   opt);
    168 
    169 	krb5_get_init_creds_opt_free(context, opt);
    170 	switch (ret) {
    171 	case 0:
    172 		break;
    173 
    174 	case KRB5_LIBOS_PWDINTR :
    175 		/* XXX */
    176 		goto bad;
    177 
    178 	case KRB5KRB_AP_ERR_BAD_INTEGRITY :
    179 	case KRB5KRB_AP_ERR_MODIFIED :
    180 		fprintf(stderr, "Password incorrect\n");
    181 		goto bad;
    182 
    183 	default:
    184 		pwkrb5_warn("failed to get credentials", context, ret);
    185 		goto bad;
    186  	}
    187 
    188 	krb5_data_zero(&result_code_string);
    189 	krb5_data_zero(&result_string);
    190 
    191 	/* XXX use getpass? It has a broken interface. */
    192 	if (UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf),
    193 				   "New password: ", 1) != 0)
    194 		goto bad;
    195 
    196 	ret = krb5_set_password(context, &cred, pwbuf, NULL,
    197 				&result_code,
    198 				&result_code_string,
    199 				&result_string);
    200 	if (ret) {
    201 		pwkrb5_warn("unable to set password", context, ret);
    202 		goto bad;
    203 	}
    204 
    205 	printf("%s%s%.*s\n",
    206 	    krb5_passwd_result_to_string(context, result_code),
    207 	    result_string.length > 0 ? " : " : "",
    208 	    (int)result_string.length,
    209 	    result_string.length > 0 ? (char *)result_string.data : "");
    210 
    211 	krb5_data_free(&result_code_string);
    212 	krb5_data_free(&result_string);
    213 
    214 	krb5_free_cred_contents(context, &cred);
    215 	krb5_free_context(context);
    216 	if (result_code)
    217 		exit(1);
    218 	return;
    219 
    220  bad:
    221 	krb5_free_context(context);
    222 	exit(1);
    223 }
    224 
    225 #else /* ! USE_PAM */
    226 
    227 static krb5_context defcontext;
    228 static krb5_principal defprinc;
    229 static int kusage = PW_USE;
    230 
    231 int
    232 krb5_init(const char *progname)
    233 {
    234     return krb5_init_context(&defcontext);
    235 }
    236 
    237 int
    238 krb5_arg (char ch, const char *opt)
    239 {
    240     krb5_error_code ret;
    241     switch(ch) {
    242     case '5':
    243     case 'k':
    244 	kusage = PW_USE_FORCE;
    245 	return 1;
    246     case 'u':
    247 	ret = krb5_parse_name(defcontext, opt, &defprinc);
    248 	if(ret) {
    249 	    krb5_warn(defcontext, ret, "%s", opt);
    250 	    return 0;
    251 	}
    252 	return 1;
    253     }
    254     return 0;
    255 }
    256 
    257 int
    258 krb5_arg_end(void)
    259 {
    260     return kusage;
    261 }
    262 
    263 void
    264 krb5_end(void)
    265 {
    266     if (defcontext == NULL)
    267 	return;
    268     if(defprinc)
    269 	krb5_free_principal(defcontext, defprinc);
    270     krb5_free_context(defcontext);
    271 }
    272 
    273 int
    274 krb5_chpw(const char *username)
    275 {
    276     krb5_error_code ret;
    277     krb5_context context;
    278     krb5_principal principal;
    279     krb5_get_init_creds_opt *opt;
    280     krb5_creds cred;
    281     int result_code;
    282     krb5_data result_code_string, result_string;
    283     char pwbuf[BUFSIZ];
    284 
    285     ret = krb5_init_context (&context);
    286     if (ret) {
    287 	pwkrb5_warn("failed kerberos initialisation", context, ret);
    288 	return 1;
    289     }
    290 
    291     ret = krb5_get_init_creds_opt_alloc (context, &opt);
    292     if (ret) {
    293 	pwkrb5_warn("failed to allocate credential opt", context, ret);
    294 	return 1;
    295     }
    296 
    297     krb5_get_init_creds_opt_set_tkt_life (opt, 300);
    298     krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
    299     krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
    300 
    301     if(username != NULL) {
    302         ret = krb5_parse_name (context, username, &principal);
    303         if (ret) {
    304 	    krb5_get_init_creds_opt_free (context, opt);
    305 	    pwkrb5_warn("failed to parse principal", context, ret);
    306 	    return 1;
    307 	}
    308     } else
    309         principal = defprinc;
    310 
    311     ret = krb5_get_init_creds_password (context,
    312                                         &cred,
    313                                         principal,
    314                                         NULL,
    315                                         krb5_prompter_posix,
    316                                         NULL,
    317                                         0,
    318                                         "kadmin/changepw",
    319                                         opt);
    320 
    321     krb5_get_init_creds_opt_free (context, opt);
    322     switch (ret) {
    323     case 0:
    324         break;
    325     case KRB5_LIBOS_PWDINTR :
    326 	/* XXX */
    327         return 1;
    328     case KRB5KRB_AP_ERR_BAD_INTEGRITY :
    329     case KRB5KRB_AP_ERR_MODIFIED :
    330 	fprintf(stderr, "Password incorrect\n");
    331 	return 1;
    332         break;
    333     default:
    334 	pwkrb5_warn("failed to get credentials", context, ret);
    335 	return 1;
    336     }
    337     krb5_data_zero (&result_code_string);
    338     krb5_data_zero (&result_string);
    339 
    340     /* XXX use getpass? It has a broken interface. */
    341     if(UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), "New password: ", 1) != 0)
    342         return 1;
    343 
    344     ret = krb5_set_password (context, &cred, pwbuf, NULL,
    345 			     &result_code,
    346 			     &result_code_string,
    347 			     &result_string);
    348     if (ret)
    349         krb5_err (context, 1, ret, "krb5_set_password");
    350 
    351     printf ("%s%s%.*s\n", krb5_passwd_result_to_string(context, result_code),
    352 	    result_string.length > 0 ? " : " : "",
    353 	    (int)result_string.length,
    354 	    result_string.length > 0 ? (char *)result_string.data : "");
    355 
    356     krb5_data_free (&result_code_string);
    357     krb5_data_free (&result_string);
    358 
    359     krb5_free_cred_contents (context, &cred);
    360     krb5_free_context (context);
    361     return result_code;
    362 }
    363 
    364 #endif /* USE_PAM */
    365