xref: /netbsd-src/usr.bin/passwd/krb5_passwd.c (revision 0dfe19f4d379a97fb960d18612851fdc8ed37cb8)
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
pwkrb5_warn(const char * msg,krb5_context context,krb5_error_code ret)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
pwkrb5_usage(const char * prefix)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
pwkrb5_argv0_usage(const char * prefix)70 pwkrb5_argv0_usage(const char *prefix)
71 {
72 
73 	(void) fprintf(stderr, "%s %s [principal]\n",
74 	    prefix, getprogname());
75 }
76 
77 void
pwkrb5_process(const char * username,int argc,char ** argv)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
krb5_init(const char * progname)232 krb5_init(const char *progname)
233 {
234     return krb5_init_context(&defcontext);
235 }
236 
237 int
krb5_arg(char ch,const char * opt)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
krb5_arg_end(void)258 krb5_arg_end(void)
259 {
260     return kusage;
261 }
262 
263 void
krb5_end(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
krb5_chpw(const char * username)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