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