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