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 OpenVision Technologies, Inc., All Rights Reserved.
27 */
28
29
30 #include <stdio.h>
31 #ifdef HAVE_MEMORY_H
32 #include <memory.h>
33 #endif
34 #include <time.h>
35 #include <locale.h>
36
37 #include <kadm5/admin.h>
38 #include "admin_internal.h"
39
40 #include <krb5.h>
41 #include <strings.h>
42
43 #define string_text error_message
44
45 const char *chpw_error_message(kadm5_ret_t code);
46
47 /*
48 * Function: kadm5_chpass_principal_util
49 *
50 * Purpose: Wrapper around chpass_principal. We can read new pw, change pw and return useful messages
51 *
52 * Arguments:
53 *
54 * princ (input) a krb5b_principal structure for the
55 * principal whose password we should change.
56 *
57 * new_password (input) NULL or a null terminated string with the
58 * the principal's desired new password. If new_password
59 * is NULL then this routine will read a new password.
60 *
61 * pw_ret (output) if non-NULL, points to a static buffer
62 * containing the new password (if password is prompted
63 * internally), or to the new_password argument (if
64 * that is non-NULL). If the former, then the buffer
65 * is only valid until the next call to the function,
66 * and the caller should be sure to zero it when
67 * it is no longer needed.
68 *
69 * msg_ret (output) a useful message is copied here.
70 *
71 * <return value> exit status of 0 for success, else the com err code
72 * for the last significant routine called.
73 *
74 * Requires:
75 *
76 * A msg_ret should point to a buffer large enough for the messasge.
77 *
78 * Effects:
79 *
80 * Modifies:
81 *
82 *
83 */
84
_kadm5_chpass_principal_util(void * server_handle,void * lhandle,krb5_principal princ,char * new_pw,char ** ret_pw,char * msg_ret,unsigned int msg_len)85 kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle,
86 void *lhandle,
87 krb5_principal princ,
88 char *new_pw,
89 char **ret_pw,
90 char *msg_ret,
91 unsigned int msg_len)
92 {
93 int code, code2;
94 unsigned int pwsize;
95 static char buffer[255];
96 char *new_password;
97 kadm5_principal_ent_rec princ_ent;
98 kadm5_policy_ent_rec policy_ent;
99 krb5_chgpwd_prot passwd_protocol;
100
101 _KADM5_CHECK_HANDLE(server_handle);
102
103 if (ret_pw)
104 *ret_pw = NULL;
105
106 if (new_pw != NULL) {
107 new_password = new_pw;
108 } else { /* read the password */
109 krb5_context context;
110
111 if ((code = (int) kadm5_init_krb5_context(&context)) == 0) {
112 pwsize = sizeof(buffer);
113 code = krb5_read_password(context, KADM5_PW_FIRST_PROMPT,
114 KADM5_PW_SECOND_PROMPT,
115 buffer, &pwsize);
116 krb5_free_context(context);
117 }
118
119 if (code == 0)
120 new_password = buffer;
121 else {
122 #ifdef ZEROPASSWD
123 memset(buffer, 0, sizeof(buffer));
124 #endif
125 if (code == KRB5_LIBOS_BADPWDMATCH) {
126 (void) strncpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH),
127 msg_len - 1);
128 msg_ret[msg_len - 1] = '\0';
129 return(code);
130 } else {
131 (void) strncpy(msg_ret, error_message(code), msg_len - 1);
132 (void) strncat(msg_ret, " ", msg_len - 1);
133 (void) strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_READING_PASSWORD),
134 msg_len - 1);
135 (void) strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
136 msg_len - 1);
137 msg_ret[msg_len - 1] = '\0';
138 return(code);
139 }
140 }
141 if (pwsize == 0) {
142 #ifdef ZEROPASSWD
143 memset(buffer, 0, sizeof(buffer));
144 #endif
145 strncpy(msg_ret, string_text(CHPASS_UTIL_NO_PASSWORD_READ), msg_len - 1);
146 msg_ret[msg_len - 1] = '\0';
147 return(KRB5_LIBOS_CANTREADPWD); /* could do better */
148 }
149 }
150
151 if (ret_pw)
152 *ret_pw = new_password;
153
154 passwd_protocol = _kadm5_get_kpasswd_protocol(server_handle);
155 if (passwd_protocol == KRB5_CHGPWD_CHANGEPW_V2) {
156 kadm5_ret_t srvr_rsp_code;
157 krb5_data srvr_msg;
158
159 srvr_msg.length = 0;
160 srvr_msg.data = NULL;
161
162 code = kadm5_chpass_principal_v2(server_handle, princ,
163 new_password,
164 &srvr_rsp_code,
165 &srvr_msg);
166 if (srvr_rsp_code) {
167 sprintf(msg_ret, "%s%s%.*s\n",
168 chpw_error_message(srvr_rsp_code),
169 srvr_msg.length? ": " : "",
170 srvr_msg.length,
171 srvr_msg.data ? srvr_msg.data : "");
172
173 return (srvr_rsp_code);
174 }
175 return (code);
176
177 } else if (passwd_protocol == KRB5_CHGPWD_RPCSEC) {
178 code = kadm5_chpass_principal(server_handle, princ,
179 new_password);
180
181 #ifdef ZEROPASSWD
182 if (!ret_pw)
183 memset(buffer, 0, sizeof(buffer)); /* in case we read a new password */
184 #endif
185
186 if (code == KADM5_OK) {
187 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_CHANGED), msg_len - 1);
188 msg_ret[msg_len - 1] = '\0';
189 return(0);
190 }
191
192 if ((code != KADM5_PASS_Q_TOOSHORT) &&
193 (code != KADM5_PASS_REUSE) &&(code != KADM5_PASS_Q_CLASS) &&
194 (code != KADM5_PASS_Q_DICT) && (code != KADM5_PASS_TOOSOON)) {
195 /* Can't get more info for other errors */
196 sprintf(buffer, "%s %s", error_message(code),
197 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
198 sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
199 buffer);
200 return(code);
201 }
202
203 /* Ok, we have a password quality error. Return a good message */
204
205 if (code == KADM5_PASS_REUSE) {
206 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE), msg_len - 1);
207 msg_ret[msg_len - 1] = '\0';
208 return(code);
209 }
210
211 if (code == KADM5_PASS_Q_DICT) {
212 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY),
213 msg_len - 1);
214 msg_ret[msg_len - 1] = '\0';
215 return(code);
216 }
217
218 /* Look up policy for the remaining messages */
219
220 code2 = kadm5_get_principal (lhandle, princ, &princ_ent,
221 KADM5_PRINCIPAL_NORMAL_MASK);
222 if (code2 != 0) {
223 strncpy(msg_ret, error_message(code2), msg_len - 1);
224 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
225 strncat(msg_ret, string_text(CHPASS_UTIL_GET_PRINC_INFO), msg_len - 1 - strlen(msg_ret));
226 strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret));
227 strncat(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret));
228 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
229 strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
230 msg_len - 1 - strlen(msg_ret));
231 strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret));
232 strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
233 msg_len - 1 - strlen(msg_ret));
234 strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret));
235 msg_ret[msg_len - 1] = '\0';
236 return(code);
237 }
238
239 if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) {
240 strncpy(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret));
241 strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
242 strncpy(msg_ret, string_text(CHPASS_UTIL_NO_POLICY_YET_Q_ERROR),
243 msg_len - 1 - strlen(msg_ret));
244 strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret));
245 strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
246 msg_len - 1 - strlen(msg_ret));
247 msg_ret[msg_len - 1] = '\0';
248
249 (void) kadm5_free_principal_ent(lhandle, &princ_ent);
250 return(code);
251 }
252
253 code2 = kadm5_get_policy(lhandle, princ_ent.policy,
254 &policy_ent);
255 if (code2 != 0) {
256 sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", error_message(code2),
257 string_text(CHPASS_UTIL_GET_POLICY_INFO),
258 error_message(code),
259 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
260 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED));
261 (void) kadm5_free_principal_ent(lhandle, &princ_ent);
262 return(code);
263 }
264
265 if (code == KADM5_PASS_Q_TOOSHORT) {
266 sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT),
267 policy_ent.pw_min_length);
268 (void) kadm5_free_principal_ent(lhandle, &princ_ent);
269 (void) kadm5_free_policy_ent(lhandle, &policy_ent);
270 return(code);
271 }
272
273
274 if (code == KADM5_PASS_Q_CLASS) {
275 sprintf(msg_ret, string_text(CHPASS_UTIL_TOO_FEW_CLASSES),
276 policy_ent.pw_min_classes);
277 (void) kadm5_free_principal_ent(lhandle, &princ_ent);
278 (void) kadm5_free_policy_ent(lhandle, &policy_ent);
279 return(code);
280 }
281
282 if (code == KADM5_PASS_TOOSOON) {
283 time_t until;
284 char *time_string, *ptr;
285
286 until = princ_ent.last_pwd_change + policy_ent.pw_min_life;
287
288 time_string = ctime(&until);
289 if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
290 *ptr = '\0';
291
292 sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SOON),
293 time_string);
294 (void) kadm5_free_principal_ent(lhandle, &princ_ent);
295 (void) kadm5_free_policy_ent(lhandle, &policy_ent);
296 return(code);
297 } else {
298
299 /* We should never get here, but just in case ... */
300 sprintf(buffer, "%s %s", error_message(code),
301 string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
302 sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
303 buffer);
304 (void) kadm5_free_principal_ent(lhandle, &princ_ent);
305 (void) kadm5_free_policy_ent(lhandle, &policy_ent);
306 return(code);
307 }
308 } else {
309 sprintf(msg_ret, "%s\n%s\n",
310 string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
311 "Password protocol in krb5.conf is not supported\n");
312 return (-1);
313 }
314 }
315
316 /*
317 * krb5_chpw_result_code_string
318 *
319 * convert the return code received from the password server
320 * to a human-readable string.
321 */
322 const char *
chpw_error_message(kadm5_ret_t result_code)323 chpw_error_message(kadm5_ret_t result_code)
324 {
325 switch (result_code) {
326 case KRB5_KPASSWD_MALFORMED:
327 return (dgettext(TEXT_DOMAIN, "Malformed request error"));
328 case KRB5_KPASSWD_HARDERROR:
329 return (dgettext(TEXT_DOMAIN, "Server error"));
330 case KRB5_KPASSWD_AUTHERROR:
331 return (dgettext(TEXT_DOMAIN, "Authentication error"));
332 case KRB5_KPASSWD_SOFTERROR:
333 return (dgettext(TEXT_DOMAIN, "Password change rejected"));
334 case KRB5_KPASSWD_ACCESSDENIED:
335 return (dgettext(TEXT_DOMAIN,
336 "Not authorized to change password"));
337 case KRB5_KPASSWD_BAD_VERSION:
338 return (dgettext(TEXT_DOMAIN, "Protocol version unsupported"));
339 case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
340 return (dgettext(TEXT_DOMAIN,
341 "initial flag required in changepw request"));
342 case KRB5_KPASSWD_POLICY_REJECT:
343 return (dgettext(TEXT_DOMAIN, "new password fails policy"));
344 case KRB5_KPASSWD_BAD_PRINCIPAL:
345 return (dgettext(TEXT_DOMAIN,
346 "target principal does not exist for "
347 "changepw request"));
348 case KRB5_KPASSWD_ETYPE_NOSUPP:
349 return (dgettext(TEXT_DOMAIN,
350 "changepw request key sequence has an "
351 "unsupported Etype"));
352 default:
353 return (dgettext(TEXT_DOMAIN, "Password change failed"));
354 }
355 }
356