xref: /netbsd-src/usr.bin/passwd/krb5_passwd.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /* $NetBSD: krb5_passwd.c,v 1.19 2011/04/24 21:16:43 elric 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 #ifdef USE_PAM
49 
50 void
51 pwkrb5_usage(const char *prefix)
52 {
53 
54 	(void) fprintf(stderr, "%s %s [-d krb5 | -k] [principal]\n",
55 	    prefix, getprogname());
56 }
57 
58 void
59 pwkrb5_argv0_usage(const char *prefix)
60 {
61 
62 	(void) fprintf(stderr, "%s %s [principal]\n",
63 	    prefix, getprogname());
64 }
65 
66 void
67 pwkrb5_process(const char *username, int argc, char **argv)
68 {
69 	krb5_context context;
70 	krb5_error_code ret;
71 	krb5_get_init_creds_opt *opt;
72 	krb5_principal principal;
73 	krb5_creds cred;
74 	int result_code;
75 	krb5_data result_code_string, result_string;
76 	char pwbuf[BUFSIZ];
77 	int ch;
78 	const char *errtxt;
79 
80 	while ((ch = getopt(argc, argv, "5ku:")) != -1) {
81 		switch (ch) {
82 		case '5':
83 			/*
84 			 * Compatibility option that historically
85 			 * specified to use Kerberos 5.  Silently
86 			 * ignore it.
87 			 */
88 			break;
89 
90 		case 'k':
91 			/*
92 			 * Absorb the -k that may have gotten us here.
93 			 */
94 			break;
95 
96 		case 'u':
97 			/*
98 			 * Historical option to specify principal.
99 			 */
100 			username = optarg;
101 			break;
102 
103 		default:
104 			usage();
105 			/* NOTREACHED */
106 		}
107 	}
108 
109 	argc -= optind;
110 	argv += optind;
111 
112 	switch (argc) {
113 	case 0:
114 		/* username already provided */
115 		break;
116 	case 1:
117 		/* overrides -u <principal> */
118 		username = argv[0];
119 		break;
120 	default:
121 		usage();
122 		/* NOTREACHED */
123 	}
124 
125 	ret = krb5_init_context(&context);
126 	if (ret != 0) {
127 		if (ret == ENXIO)
128 			errx(1, "Kerberos 5 not in use.");
129 		errx(1, "Unable to initialize Kerberos 5: %s", strerror(ret));
130 	}
131 
132 	ret = krb5_get_init_creds_opt_alloc(context, &opt);
133 	if (ret) {
134 		errtxt = krb5_get_error_message(context, ret);
135 		if (errtxt != NULL) {
136 			warnx("failed to allocate opts: %s", errtxt);
137 			krb5_free_error_message(context, errtxt);
138 		} else {
139 			warnx("failed to allocate opts: %d", ret);
140 		}
141 		goto bad;
142 	}
143 
144 	krb5_get_init_creds_opt_set_tkt_life(opt, 300L);
145 	krb5_get_init_creds_opt_set_forwardable(opt, FALSE);
146 	krb5_get_init_creds_opt_set_proxiable(opt, FALSE);
147 
148 	ret = krb5_parse_name(context, username, &principal);
149 	if (ret) {
150 		errtxt = krb5_get_error_message(context, ret);
151 		if (errtxt != NULL) {
152 			warnx("failed to parse principal: %s", errtxt);
153 			krb5_free_error_message(context, errtxt);
154 		} else {
155 			warnx("failed to parse principal: %d", ret);
156 		}
157 		goto bad;
158 	}
159 
160 	ret = krb5_get_init_creds_password(context,
161 					   &cred,
162 					   principal,
163 					   NULL,
164 					   krb5_prompter_posix,
165 					   NULL,
166 					   0L,
167 					   "kadmin/changepw",
168 					   opt);
169 
170 
171 	switch (ret) {
172 	case 0:
173 		break;
174 
175 	case KRB5_LIBOS_PWDINTR :
176 		/* XXX */
177 		goto bad;
178 
179 	case KRB5KRB_AP_ERR_BAD_INTEGRITY :
180 	case KRB5KRB_AP_ERR_MODIFIED :
181 		fprintf(stderr, "Password incorrect\n");
182 		goto bad;
183 
184 	default:
185 		errtxt = krb5_get_error_message(context, ret);
186 		if (errtxt != NULL) {
187 			warnx("failed to get credentials: %s", errtxt);
188 			krb5_free_error_message(context, errtxt);
189 		} else {
190 			warnx("failed to get credentials: %d", ret);
191 		}
192 		goto bad;
193  	}
194 
195 	krb5_data_zero(&result_code_string);
196 	krb5_data_zero(&result_string);
197 
198 	/* XXX use getpass? It has a broken interface. */
199 	if (UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf),
200 				   "New password: ", 1) != 0)
201 		goto bad;
202 
203 	ret = krb5_set_password(context, &cred, pwbuf, NULL,
204 				&result_code,
205 				&result_code_string,
206 				&result_string);
207 	if (ret) {
208 		errtxt = krb5_get_error_message(context, ret);
209 		if (errtxt != NULL) {
210 			warnx("unable to set password: %s", errtxt);
211 			krb5_free_error_message(context, errtxt);
212 		} else {
213 			warnx("unable to set password: %d", ret);
214 		}
215 		goto bad;
216 	}
217 
218 	printf("%s%s%.*s\n",
219 	    krb5_passwd_result_to_string(context, result_code),
220 	    result_string.length > 0 ? " : " : "",
221 	    (int)result_string.length,
222 	    result_string.length > 0 ? (char *)result_string.data : "");
223 
224 	krb5_data_free(&result_code_string);
225 	krb5_data_free(&result_string);
226 
227 	krb5_free_cred_contents(context, &cred);
228 	krb5_free_context(context);
229 	if (result_code)
230 		exit(1);
231 	return;
232 
233  bad:
234 	krb5_free_context(context);
235 	exit(1);
236 }
237 
238 #else /* ! USE_PAM */
239 
240 static krb5_context defcontext;
241 static krb5_principal defprinc;
242 static int kusage = PW_USE;
243 
244 int
245 krb5_init(const char *progname)
246 {
247     return krb5_init_context(&defcontext);
248 }
249 
250 int
251 krb5_arg (char ch, const char *opt)
252 {
253     krb5_error_code ret;
254     switch(ch) {
255     case '5':
256     case 'k':
257 	kusage = PW_USE_FORCE;
258 	return 1;
259     case 'u':
260 	ret = krb5_parse_name(defcontext, opt, &defprinc);
261 	if(ret) {
262 	    krb5_warn(defcontext, ret, "%s", opt);
263 	    return 0;
264 	}
265 	return 1;
266     }
267     return 0;
268 }
269 
270 int
271 krb5_arg_end(void)
272 {
273     return kusage;
274 }
275 
276 void
277 krb5_end(void)
278 {
279     if (defcontext == NULL)
280 	return;
281     if(defprinc)
282 	krb5_free_principal(defcontext, defprinc);
283     krb5_free_context(defcontext);
284 }
285 
286 
287 int
288 krb5_chpw(const char *username)
289 {
290     krb5_error_code ret;
291     krb5_context context;
292     krb5_principal principal;
293     krb5_get_init_creds_opt opt;
294     krb5_creds cred;
295     int result_code;
296     krb5_data result_code_string, result_string;
297     char pwbuf[BUFSIZ];
298     const char *errtxt;
299 
300     ret = krb5_init_context (&context);
301     if (ret) {
302 	errtxt = krb5_get_error_message(context, ret);
303 	if (errtxt != NULL) {
304 	    warnx("failed kerberos initialisation: %s", errtxt);
305 	    krb5_free_error_message(context, errtxt);
306 	} else {
307 	    warnx("failed kerberos initialisation: %d", ret);
308 	}
309 	return 1;
310     }
311 
312     krb5_get_init_creds_opt_init (&opt);
313 
314     krb5_get_init_creds_opt_set_tkt_life (&opt, 300);
315     krb5_get_init_creds_opt_set_forwardable (&opt, FALSE);
316     krb5_get_init_creds_opt_set_proxiable (&opt, FALSE);
317 
318     if(username != NULL) {
319         ret = krb5_parse_name (context, username, &principal);
320         if (ret) {
321 	    errtxt = krb5_get_error_message(context, ret);
322 	    if (errtxt != NULL) {
323 		warnx("failed to parse principal: %s", errtxt);
324 		krb5_free_error_message(context, errtxt);
325 	    } else {
326 		warnx("failed to parse principal: %d", ret);
327 	    }
328 	    return 1;
329 	}
330     } else
331         principal = defprinc;
332 
333     ret = krb5_get_init_creds_password (context,
334                                         &cred,
335                                         principal,
336                                         NULL,
337                                         krb5_prompter_posix,
338                                         NULL,
339                                         0,
340                                         "kadmin/changepw",
341                                         &opt);
342 
343     switch (ret) {
344     case 0:
345         break;
346     case KRB5_LIBOS_PWDINTR :
347 	/* XXX */
348         return 1;
349     case KRB5KRB_AP_ERR_BAD_INTEGRITY :
350     case KRB5KRB_AP_ERR_MODIFIED :
351 	fprintf(stderr, "Password incorrect\n");
352 	return 1;
353         break;
354     default:
355 	errtxt = krb5_get_error_message(context, ret);
356 	if (errtxt != NULL) {
357 	    warnx("failed to get credentials: %s", errtxt);
358 	    krb5_free_error_message(context, errtxt);
359 	} else {
360 	    warnx("failed to get credentials: %d", ret);
361 	}
362 	return 1;
363     }
364     krb5_data_zero (&result_code_string);
365     krb5_data_zero (&result_string);
366 
367     /* XXX use getpass? It has a broken interface. */
368     if(UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), "New password: ", 1) != 0)
369         return 1;
370 
371     ret = krb5_set_password (context, &cred, pwbuf, NULL,
372 			     &result_code,
373 			     &result_code_string,
374 			     &result_string);
375     if (ret)
376         krb5_err (context, 1, ret, "krb5_set_password");
377 
378     printf ("%s%s%.*s\n", krb5_passwd_result_to_string(context, result_code),
379 	    result_string.length > 0 ? " : " : "",
380 	    (int)result_string.length,
381 	    result_string.length > 0 ? (char *)result_string.data : "");
382 
383     krb5_data_free (&result_code_string);
384     krb5_data_free (&result_string);
385 
386     krb5_free_cred_contents (context, &cred);
387     krb5_free_context (context);
388     return result_code;
389 }
390 
391 #endif /* USE_PAM */
392