1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6
7 /*
8 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
9 *
10 * Openvision retains the copyright to derivative works of
11 * this source code. Do *NOT* create a derivative of this
12 * source code before consulting with your legal department.
13 * Do *NOT* integrate *ANY* of this source code into another
14 * product before consulting with your legal department.
15 *
16 * For further information, read the top-level Openvision
17 * copyright which is contained in the top-level MIT Kerberos
18 * copyright.
19 *
20 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
21 *
22 */
23
24
25 /*
26 * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
27 *
28 * $Header$
29 *
30 *
31 */
32
33 static char rcsid[] = "$Id: kpasswd.c 17258 2005-06-21 01:36:03Z raeburn $";
34
35 #include <kadm5/admin.h>
36 #include <krb5.h>
37
38 #include "kpasswd_strings.h"
39 #define string_text error_message
40
41 #include "kpasswd.h"
42
43 #include <stdio.h>
44 #include <pwd.h>
45 #include <string.h>
46 #include <libintl.h>
47
48 extern char *whoami;
49
50 extern void display_intro_message();
51 extern long read_old_password();
52 extern long read_new_password();
53
54 #define MISC_EXIT_STATUS 6
55
56 /*
57 * Function: kpasswd
58 *
59 * Purpose: Initialize and call lower level routines to change a password
60 *
61 * Arguments:
62 *
63 * context (r) krb5_context to use
64 * argc/argv (r) principal name to use, optional
65 * read_old_password (f) function to read old password
66 * read_new_password (f) function to read new and change password
67 * display_intro_message (f) function to display intro message
68 * whoami (extern) argv[0]
69 *
70 * Returns:
71 * exit status of 0 for success
72 * 1 principal unknown
73 * 2 old password wrong
74 * 3 cannot initialize admin server session
75 * 4 new passwd mismatch or error trying to change pw
76 * 5 password not typed
77 * 6 misc error
78 * 7 incorrect usage
79 *
80 * Requires:
81 * Passwords cannot be more than 255 characters long.
82 *
83 * Effects:
84 *
85 * If argc is 2, the password for the principal specified in argv[1]
86 * is changed; otherwise, the principal of the default credential
87 * cache or username is used. display_intro_message is called with
88 * the arguments KPW_STR_CHANGING_PW_FOR and the principal name.
89 * read_old_password is then called to prompt for the old password.
90 * The admin system is then initialized, the principal's policy
91 * retrieved and explained, if appropriate, and finally
92 * read_new_password is called to read the new password and change the
93 * principal's password (presumably ovsec_kadm_chpass_principal).
94 * admin system is de-initialized before the function returns.
95 *
96 * Modifies:
97 *
98 * Changes the principal's password.
99 *
100 */
101 int
kpasswd(context,argc,argv)102 kpasswd(context, argc, argv)
103 krb5_context context;
104 int argc;
105 char *argv[];
106 {
107 kadm5_ret_t code;
108 krb5_ccache ccache = NULL;
109 krb5_principal princ = 0;
110 char *princ_str;
111 struct passwd *pw = 0;
112 unsigned int pwsize;
113 char password[255]; /* I don't really like 255 but that's what kinit uses */
114 char msg_ret[1024], admin_realm[1024];
115 kadm5_principal_ent_rec principal_entry;
116 kadm5_policy_ent_rec policy_entry;
117 void *server_handle;
118 kadm5_config_params params;
119 char *cpw_service;
120
121 memset((char *)¶ms, 0, sizeof (params));
122 memset(&principal_entry, 0, sizeof (principal_entry));
123 memset(&policy_entry, 0, sizeof (policy_entry));
124
125 if (argc > 2) {
126 com_err(whoami, KPW_STR_USAGE, 0);
127 return(7);
128 /*NOTREACHED*/
129 }
130
131 /************************************
132 * Get principal name to change *
133 ************************************/
134
135 /* Look on the command line first, followed by the default credential
136 cache, followed by defaulting to the Unix user name */
137
138 if (argc == 2)
139 princ_str = strdup(argv[1]);
140 else {
141 code = krb5_cc_default(context, &ccache);
142 /* If we succeed, find who is in the credential cache */
143 if (code == 0) {
144 /* Get default principal from cache if one exists */
145 code = krb5_cc_get_principal(context, ccache, &princ);
146 /* if we got a principal, unparse it, otherwise get out of the if
147 with an error code */
148 (void) krb5_cc_close(context, ccache);
149 if (code == 0) {
150 code = krb5_unparse_name(context, princ, &princ_str);
151 if (code != 0) {
152 com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME));
153 return(MISC_EXIT_STATUS);
154 }
155 }
156 }
157
158 /* this is a crock.. we want to compare against */
159 /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */
160 /* both the file and stdio types return FCC_NOFILE. If there is */
161 /* ever another ccache type (or if the error codes are ever */
162 /* fixed), this code will have to be updated. */
163 if (code && code != KRB5_FCC_NOFILE) {
164 com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC));
165 return(MISC_EXIT_STATUS);
166 }
167
168 /* if either krb5_cc failed check the passwd file */
169 if (code != 0) {
170 pw = getpwuid( getuid());
171 if (pw == NULL) {
172 com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE));
173 return(MISC_EXIT_STATUS);
174 }
175 princ_str = strdup(pw->pw_name);
176 }
177 }
178
179 display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str);
180
181 /* Need to get a krb5_principal, unless we started from with one from
182 the credential cache */
183
184 if (! princ) {
185 code = krb5_parse_name (context, princ_str, &princ);
186 if (code != 0) {
187 com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str);
188 free(princ_str);
189 return(MISC_EXIT_STATUS);
190 }
191 }
192
193 pwsize = sizeof(password);
194 code = read_old_password(context, password, &pwsize);
195
196 if (code != 0) {
197 memset(password, 0, sizeof(password));
198 com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD));
199 krb5_free_principal(context, princ);
200 free(princ_str);
201 return(MISC_EXIT_STATUS);
202 }
203 if (pwsize == 0) {
204 memset(password, 0, sizeof(password));
205 com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ));
206 krb5_free_principal(context, princ);
207 free(princ_str);
208 return(5);
209 }
210
211 snprintf(admin_realm, sizeof (admin_realm),
212 krb5_princ_realm(context, princ)->data);
213 params.mask |= KADM5_CONFIG_REALM;
214 params.realm = admin_realm;
215
216
217 if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
218 fprintf(stderr, gettext("%s: unable to get host based "
219 "service name for realm %s\n"),
220 whoami, admin_realm);
221 exit(1);
222 }
223
224 code = kadm5_init_with_password(princ_str, password, cpw_service,
225 ¶ms, KADM5_STRUCT_VERSION,
226 KADM5_API_VERSION_2, NULL,
227 &server_handle);
228 free(cpw_service);
229 if (code != 0) {
230 if (code == KADM5_BAD_PASSWORD)
231 com_err(whoami, 0,
232 string_text(KPW_STR_OLD_PASSWORD_INCORRECT));
233 else
234 com_err(whoami, 0,
235 string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER),
236 admin_realm,
237 error_message(code));
238 krb5_free_principal(context, princ);
239 free(princ_str);
240 return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
241 }
242
243 /*
244 * we can only check the policy if the server speaks
245 * RPCSEC_GSS
246 */
247 if (_kadm5_get_kpasswd_protocol(server_handle) == KRB5_CHGPWD_RPCSEC) {
248 /* Explain policy restrictions on new password if any. */
249 /*
250 * Note: copy of this exists in login
251 * (kverify.c/get_verified_in_tkt).
252 */
253
254 code = kadm5_get_principal(server_handle, princ,
255 &principal_entry,
256 KADM5_PRINCIPAL_NORMAL_MASK);
257 if (code != 0) {
258 com_err(whoami, 0,
259 string_text((code == KADM5_UNK_PRINC)
260 ? KPW_STR_PRIN_UNKNOWN :
261 KPW_STR_CANT_GET_POLICY_INFO),
262 princ_str);
263 krb5_free_principal(context, princ);
264 free(princ_str);
265 (void) kadm5_destroy(server_handle);
266 return ((code == KADM5_UNK_PRINC) ? 1 :
267 MISC_EXIT_STATUS);
268 }
269 if ((principal_entry.aux_attributes & KADM5_POLICY) != 0) {
270 code = kadm5_get_policy(server_handle,
271 principal_entry.policy,
272 &policy_entry);
273 if (code != 0) {
274 /*
275 * doesn't matter which error comes back,
276 * there's no nice recovery or need to
277 * differentiate to the user
278 */
279 com_err(whoami, 0,
280 string_text(KPW_STR_CANT_GET_POLICY_INFO),
281 princ_str);
282 (void) kadm5_free_principal_ent(server_handle,
283 &principal_entry);
284 krb5_free_principal(context, princ);
285 free(princ_str);
286 free(princ_str);
287 (void) kadm5_destroy(server_handle);
288 return (MISC_EXIT_STATUS);
289 }
290 com_err(whoami, 0,
291 string_text(KPW_STR_POLICY_EXPLANATION),
292 princ_str, principal_entry.policy,
293 policy_entry.pw_min_length,
294 policy_entry.pw_min_classes);
295 if (code = kadm5_free_principal_ent(server_handle,
296 &principal_entry)) {
297 (void) kadm5_free_policy_ent(server_handle,
298 &policy_entry);
299 krb5_free_principal(context, princ);
300 free(princ_str);
301 com_err(whoami, code,
302 string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
303 (void) kadm5_destroy(server_handle);
304 return (MISC_EXIT_STATUS);
305 }
306 if (code = kadm5_free_policy_ent(server_handle,
307 &policy_entry)) {
308 krb5_free_principal(context, princ);
309 free(princ_str);
310 com_err(whoami, code,
311 string_text(KPW_STR_WHILE_FREEING_POLICY));
312 (void) kadm5_destroy(server_handle);
313 return (MISC_EXIT_STATUS);
314 }
315 } else {
316 /*
317 * kpasswd *COULD* output something here to
318 * encourage the choice of good passwords,
319 * in the absence of an enforced policy.
320 */
321 if (code = kadm5_free_principal_ent(server_handle,
322 &principal_entry)) {
323 krb5_free_principal(context, princ);
324 free(princ_str);
325 com_err(whoami, code,
326 string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
327 (void) kadm5_destroy(server_handle);
328 return (MISC_EXIT_STATUS);
329 }
330 }
331 } /* if protocol == KRB5_CHGPWD_RPCSEC */
332
333 pwsize = sizeof(password);
334 code = read_new_password(server_handle, password, &pwsize, msg_ret, sizeof (msg_ret), princ);
335 memset(password, 0, sizeof(password));
336
337 if (code)
338 com_err(whoami, 0, msg_ret);
339
340 krb5_free_principal(context, princ);
341 free(princ_str);
342
343 (void) kadm5_destroy(server_handle);
344
345 if (code == KRB5_LIBOS_CANTREADPWD)
346 return(5);
347 else if (code)
348 return(4);
349 else
350 return(0);
351 }
352