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