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