krb5_passwd.c revision 1.13 1 /* $NetBSD: krb5_passwd.c,v 1.13 2005/02/26 07:19:25 thorpej 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, 300);
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 0,
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 break;
177
178 default:
179 warnx("failed to get credentials: %s",
180 krb5_get_err_text(context, ret));
181 goto bad;
182 }
183
184 krb5_data_zero(&result_code_string);
185 krb5_data_zero(&result_string);
186
187 /* XXX use getpass? It has a broken interface. */
188 if (UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf),
189 "New password: ", 1) != 0)
190 goto bad;
191
192 ret = krb5_set_password(context, &cred, pwbuf, NULL,
193 &result_code,
194 &result_code_string,
195 &result_string);
196 if (ret) {
197 warnx("unable to set password: %s",
198 krb5_get_err_text(context, ret));
199 goto bad;
200 }
201
202 printf("%s%s%.*s\n",
203 krb5_passwd_result_to_string(context, result_code),
204 result_string.length > 0 ? " : " : "",
205 (int)result_string.length,
206 result_string.length > 0 ? (char *)result_string.data : "");
207
208 krb5_data_free(&result_code_string);
209 krb5_data_free(&result_string);
210
211 krb5_free_creds_contents(context, &cred);
212 krb5_free_context(context);
213 if (result_code)
214 exit(1);
215 return;
216
217 bad:
218 krb5_free_context(context);
219 exit(1);
220 }
221
222 #else /* ! USE_PAM */
223
224 static krb5_context context;
225 static krb5_principal defprinc;
226 static int kusage = PW_USE;
227
228 int
229 krb5_init(const char *progname)
230 {
231 return krb5_init_context(&context);
232 }
233
234 int
235 krb5_arg (char ch, const char *optarg)
236 {
237 krb5_error_code ret;
238 switch(ch) {
239 case '5':
240 case 'k':
241 kusage = PW_USE_FORCE;
242 return 1;
243 case 'u':
244 ret = krb5_parse_name(context, optarg, &defprinc);
245 if(ret) {
246 krb5_warn(context, ret, "%s", optarg);
247 return 0;
248 }
249 return 1;
250 }
251 return 0;
252 }
253
254 int
255 krb5_arg_end(void)
256 {
257 return kusage;
258 }
259
260 void
261 krb5_end(void)
262 {
263 if (context == NULL)
264 return;
265 if(defprinc)
266 krb5_free_principal(context, defprinc);
267 krb5_free_context(context);
268 }
269
270
271 int
272 krb5_chpw(const char *username)
273 {
274 krb5_error_code ret;
275 krb5_context context;
276 krb5_principal principal;
277 krb5_get_init_creds_opt opt;
278 krb5_creds cred;
279 int result_code;
280 krb5_data result_code_string, result_string;
281 char pwbuf[BUFSIZ];
282
283 ret = krb5_init_context (&context);
284 if (ret) {
285 warnx("failed kerberos initialisation: %s",
286 krb5_get_err_text(context, ret));
287 return 1;
288 }
289
290 krb5_get_init_creds_opt_init (&opt);
291
292 krb5_get_init_creds_opt_set_tkt_life (&opt, 300);
293 krb5_get_init_creds_opt_set_forwardable (&opt, FALSE);
294 krb5_get_init_creds_opt_set_proxiable (&opt, FALSE);
295
296 if(username != NULL) {
297 ret = krb5_parse_name (context, username, &principal);
298 if (ret) {
299 warnx("failed to parse principal: %s",
300 krb5_get_err_text(context, ret));
301 return 1;
302 }
303 } else
304 principal = defprinc;
305
306 ret = krb5_get_init_creds_password (context,
307 &cred,
308 principal,
309 NULL,
310 krb5_prompter_posix,
311 NULL,
312 0,
313 "kadmin/changepw",
314 &opt);
315
316 switch (ret) {
317 case 0:
318 break;
319 case KRB5_LIBOS_PWDINTR :
320 /* XXX */
321 return 1;
322 case KRB5KRB_AP_ERR_BAD_INTEGRITY :
323 case KRB5KRB_AP_ERR_MODIFIED :
324 fprintf(stderr, "Password incorrect\n");
325 return 1;
326 break;
327 default:
328 warnx("failed to get credentials: %s",
329 krb5_get_err_text(context, ret));
330 return 1;
331 }
332 krb5_data_zero (&result_code_string);
333 krb5_data_zero (&result_string);
334
335 /* XXX use getpass? It has a broken interface. */
336 if(UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), "New password: ", 1) != 0)
337 return 1;
338
339 ret = krb5_set_password (context, &cred, pwbuf, NULL,
340 &result_code,
341 &result_code_string,
342 &result_string);
343 if (ret)
344 krb5_err (context, 1, ret, "krb5_set_password");
345
346 printf ("%s%s%.*s\n", krb5_passwd_result_to_string(context, result_code),
347 result_string.length > 0 ? " : " : "",
348 (int)result_string.length,
349 result_string.length > 0 ? (char *)result_string.data : "");
350
351 krb5_data_free (&result_code_string);
352 krb5_data_free (&result_string);
353
354 krb5_free_creds_contents (context, &cred);
355 krb5_free_context (context);
356 return result_code;
357 }
358
359 #endif /* USE_PAM */
360