xref: /illumos-gate/usr/src/lib/krb5/kadm5/chpass_util.c (revision 34a0f871d192b33b865455a8812a3d34c1866315)
1 /*
2  * Copyright 2006 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 #ifdef HAVE_MEMORY_H
37 #include <memory.h>
38 #endif
39 #include <time.h>
40 #include <locale.h>
41 
42 #include <kadm5/admin.h>
43 #include "admin_internal.h"
44 
45 #include <krb5.h>
46 
47 #define string_text error_message
48 
49 const char *chpw_error_message(kadm5_ret_t code);
50 
51 /*
52  * Function: kadm5_chpass_principal_util
53  *
54  * Purpose: Wrapper around chpass_principal. We can read new pw, change pw and return useful messages
55  *
56  * Arguments:
57  *
58  *      princ          (input) a krb5b_principal structure for the
59  *                     principal whose password we should change.
60  *
61  *      new_password   (input) NULL or a null terminated string with the
62  *                     the principal's desired new password.  If new_password
63  *                     is NULL then this routine will read a new password.
64  *
65  *	pw_ret		(output) if non-NULL, points to a static buffer
66  *			containing the new password (if password is prompted
67  *			internally), or to the new_password argument (if
68  *			that is non-NULL).  If the former, then the buffer
69  *			is only valid until the next call to the function,
70  *			and the caller should be sure to zero it when
71  *			it is no longer needed.
72  *
73  *      msg_ret         (output) a useful message is copied here.
74  *
75  *      <return value>  exit status of 0 for success, else the com err code
76  *                      for the last significant routine called.
77  *
78  * Requires:
79  *
80  *      A msg_ret should point to a buffer large enough for the messasge.
81  *
82  * Effects:
83  *
84  * Modifies:
85  *
86  *
87  */
88 
89 kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle,
90 					 void *lhandle,
91 					 krb5_principal princ,
92 					 char *new_pw,
93 					 char **ret_pw,
94 					 char *msg_ret,
95 					 unsigned int msg_len)
96 {
97   int code, code2;
98   unsigned int pwsize;
99   static char buffer[255];
100   char *new_password;
101   kadm5_principal_ent_rec princ_ent;
102   kadm5_policy_ent_rec policy_ent;
103   krb5_chgpwd_prot passwd_protocol;
104 
105   _KADM5_CHECK_HANDLE(server_handle);
106 
107   if (ret_pw)
108     *ret_pw = NULL;
109 
110   if (new_pw != NULL) {
111     new_password = new_pw;
112   } else { /* read the password */
113     krb5_context context;
114 
115     if ((code = (int) krb5_init_context(&context)) == 0) {
116       pwsize = sizeof(buffer);
117       code = krb5_read_password(context, 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)); /* in case we read a new password */
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) &&(code != KADM5_PASS_Q_CLASS) &&
198       (code != KADM5_PASS_Q_DICT) && (code != KADM5_PASS_TOOSOON)) {
199     /* Can't get more info for other errors */
200     sprintf(buffer, "%s %s", error_message(code),
201 	    string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
202     sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
203 	    buffer);
204     return(code);
205   }
206 
207   /* Ok, we have a password quality error. Return a good message */
208 
209   if (code == KADM5_PASS_REUSE) {
210     strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE), msg_len - 1);
211     msg_ret[msg_len - 1] = '\0';
212     return(code);
213   }
214 
215   if (code == KADM5_PASS_Q_DICT) {
216     strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY),
217 	    msg_len - 1);
218     msg_ret[msg_len - 1] = '\0';
219     return(code);
220   }
221 
222   /* Look up policy for the remaining messages */
223 
224   code2 = kadm5_get_principal (lhandle, princ, &princ_ent,
225 			       KADM5_PRINCIPAL_NORMAL_MASK);
226   if (code2 != 0) {
227     strncpy(msg_ret, error_message(code2), msg_len - 1);
228     strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
229     strncat(msg_ret, string_text(CHPASS_UTIL_GET_PRINC_INFO), msg_len - 1 - strlen(msg_ret));
230     strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret));
231     strncat(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret));
232     strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
233     strncat(msg_ret, string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
234 	    msg_len - 1 - strlen(msg_ret));
235     strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret));
236     strncat(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
237 	    msg_len - 1 - strlen(msg_ret));
238     strncat(msg_ret, "\n", msg_len - 1 - strlen(msg_ret));
239     msg_ret[msg_len - 1] = '\0';
240     return(code);
241   }
242 
243   if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) {
244     strncpy(msg_ret, error_message(code), msg_len - 1 - strlen(msg_ret));
245     strncat(msg_ret, " ", msg_len - 1 - strlen(msg_ret));
246     strncpy(msg_ret, string_text(CHPASS_UTIL_NO_POLICY_YET_Q_ERROR),
247 	    msg_len - 1 - strlen(msg_ret));
248     strncat(msg_ret, "\n\n", msg_len - 1 - strlen(msg_ret));
249     strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
250 	    msg_len - 1 - strlen(msg_ret));
251     msg_ret[msg_len - 1] = '\0';
252 
253     (void) kadm5_free_principal_ent(lhandle, &princ_ent);
254     return(code);
255   }
256 
257   code2 = kadm5_get_policy(lhandle, princ_ent.policy,
258 			   &policy_ent);
259   if (code2 != 0) {
260     sprintf(msg_ret, "%s %s\n%s %s\n\n%s\n ", error_message(code2),
261 	    string_text(CHPASS_UTIL_GET_POLICY_INFO),
262 	    error_message(code),
263 	    string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
264 	    string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED));
265     (void) kadm5_free_principal_ent(lhandle, &princ_ent);
266     return(code);
267   }
268 
269   if (code == KADM5_PASS_Q_TOOSHORT) {
270     sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT),
271 	    policy_ent.pw_min_length);
272     (void) kadm5_free_principal_ent(lhandle, &princ_ent);
273     (void) kadm5_free_policy_ent(lhandle, &policy_ent);
274     return(code);
275   }
276 
277 
278   if (code == KADM5_PASS_Q_CLASS) {
279     sprintf(msg_ret, string_text(CHPASS_UTIL_TOO_FEW_CLASSES),
280 	    policy_ent.pw_min_classes);
281     (void) kadm5_free_principal_ent(lhandle, &princ_ent);
282     (void) kadm5_free_policy_ent(lhandle, &policy_ent);
283     return(code);
284   }
285 
286   if (code == KADM5_PASS_TOOSOON) {
287     time_t until;
288     char *time_string, *ptr;
289 
290     until = princ_ent.last_pwd_change + policy_ent.pw_min_life;
291 
292     time_string = ctime(&until);
293     if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
294       *ptr = '\0';
295 
296     sprintf(msg_ret, string_text(CHPASS_UTIL_PASSWORD_TOO_SOON),
297 	    time_string);
298     (void) kadm5_free_principal_ent(lhandle, &princ_ent);
299     (void) kadm5_free_policy_ent(lhandle, &policy_ent);
300     return(code);
301 		} else {
302 
303   /* We should never get here, but just in case ... */
304   sprintf(buffer, "%s %s", error_message(code),
305 	  string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
306   sprintf(msg_ret, "%s\n%s\n", string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
307 	  buffer);
308   (void) kadm5_free_principal_ent(lhandle, &princ_ent);
309   (void) kadm5_free_policy_ent(lhandle, &policy_ent);
310   return(code);
311 		}
312 	} else {
313 		sprintf(msg_ret, "%s\n%s\n",
314 			string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
315 			"Password protocol in krb5.conf is not supported\n");
316 		return (-1);
317 	}
318 }
319 
320 /*
321  * krb5_chpw_result_code_string
322  *
323  * convert the return code received from the password server
324  * to a human-readable string.
325  */
326 const char *
327 chpw_error_message(kadm5_ret_t result_code)
328 {
329 	switch (result_code) {
330 	case KRB5_KPASSWD_MALFORMED:
331 		return (dgettext(TEXT_DOMAIN, "Malformed request error"));
332 	case KRB5_KPASSWD_HARDERROR:
333 		return (dgettext(TEXT_DOMAIN, "Server error"));
334 	case KRB5_KPASSWD_AUTHERROR:
335 		return (dgettext(TEXT_DOMAIN, "Authentication error"));
336 	case KRB5_KPASSWD_SOFTERROR:
337 		return (dgettext(TEXT_DOMAIN, "Password change rejected"));
338 	case KRB5_KPASSWD_ACCESSDENIED:
339 		return (dgettext(TEXT_DOMAIN,
340 				"Not authorized to change password"));
341 	case KRB5_KPASSWD_BAD_VERSION:
342 		return (dgettext(TEXT_DOMAIN, "Protocol version unsupported"));
343 	case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
344 		return (dgettext(TEXT_DOMAIN,
345 				"initial flag required in changepw request"));
346 	case KRB5_KPASSWD_POLICY_REJECT:
347 		return (dgettext(TEXT_DOMAIN, "new password fails policy"));
348 	case KRB5_KPASSWD_BAD_PRINCIPAL:
349 		return (dgettext(TEXT_DOMAIN,
350 		    "target principal does not exist for "
351 		    "changepw request"));
352 	case KRB5_KPASSWD_ETYPE_NOSUPP:
353 		return (dgettext(TEXT_DOMAIN,
354 		    "changepw request key sequence has an "
355 		    "unsupported Etype"));
356 	default:
357 		return (dgettext(TEXT_DOMAIN, "Password change failed"));
358 	}
359 }
360