1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
4  */
5 
6 
7 #include "k5-int.h"
8 
9 #include <kadm5/admin.h>
10 #include "admin_internal.h"
11 
12 
13 #define string_text error_message
14 
15 /*
16  * Function: kadm5_chpass_principal_util
17  *
18  * Purpose: Wrapper around chpass_principal. We can read new pw, change pw and return useful messages
19  *
20  * Arguments:
21  *
22  *      princ          (input) a krb5b_principal structure for the
23  *                     principal whose password we should change.
24  *
25  *      new_password   (input) NULL or a null terminated string with the
26  *                     the principal's desired new password.  If new_password
27  *                     is NULL then this routine will read a new password.
28  *
29  *      pw_ret          (output) if non-NULL, points to a static buffer
30  *                      containing the new password (if password is prompted
31  *                      internally), or to the new_password argument (if
32  *                      that is non-NULL).  If the former, then the buffer
33  *                      is only valid until the next call to the function,
34  *                      and the caller should be sure to zero it when
35  *                      it is no longer needed.
36  *
37  *      msg_ret         (output) a useful message is copied here.
38  *
39  *      <return value>  exit status of 0 for success, else the com err code
40  *                      for the last significant routine called.
41  *
42  * Requires:
43  *
44  *      A msg_ret should point to a buffer large enough for the messasge.
45  *
46  * Effects:
47  *
48  * Modifies:
49  *
50  *
51  */
52 
_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)53 kadm5_ret_t _kadm5_chpass_principal_util(void *server_handle,
54                                          void *lhandle,
55                                          krb5_principal princ,
56                                          char *new_pw,
57                                          char **ret_pw,
58                                          char *msg_ret,
59                                          unsigned int msg_len)
60 {
61     int code, code2;
62     unsigned int pwsize;
63     static char buffer[255];
64     char *new_password;
65     kadm5_principal_ent_rec princ_ent;
66     kadm5_policy_ent_rec policy_ent;
67 
68     _KADM5_CHECK_HANDLE(server_handle);
69 
70     if (ret_pw)
71         *ret_pw = NULL;
72 
73     if (new_pw != NULL) {
74         new_password = new_pw;
75     } else { /* read the password */
76         krb5_context context;
77 
78         if ((code = (int) kadm5_init_krb5_context(&context)) == 0) {
79             pwsize = sizeof(buffer);
80             code = krb5_read_password(context, KADM5_PW_FIRST_PROMPT,
81                                       KADM5_PW_SECOND_PROMPT,
82                                       buffer, &pwsize);
83             krb5_free_context(context);
84         }
85 
86         if (code == 0)
87             new_password = buffer;
88         else {
89 #ifdef ZEROPASSWD
90             memset(buffer, 0, sizeof(buffer));
91 #endif
92             if (code == KRB5_LIBOS_BADPWDMATCH) {
93                 strncpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH),
94                         msg_len - 1);
95                 msg_ret[msg_len - 1] = '\0';
96                 return(code);
97             } else {
98                 snprintf(msg_ret, msg_len, "%s %s\n\n%s",
99                          error_message(code),
100                          string_text(CHPASS_UTIL_WHILE_READING_PASSWORD),
101                          string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED));
102                 msg_ret[msg_len - 1] = '\0';
103                 return(code);
104             }
105         }
106         if (pwsize == 0) {
107 #ifdef ZEROPASSWD
108             memset(buffer, 0, sizeof(buffer));
109 #endif
110             strncpy(msg_ret, string_text(CHPASS_UTIL_NO_PASSWORD_READ), msg_len - 1);
111             msg_ret[msg_len - 1] = '\0';
112             return(KRB5_LIBOS_CANTREADPWD); /* could do better */
113         }
114     }
115 
116     if (ret_pw)
117         *ret_pw = new_password;
118 
119     code = kadm5_chpass_principal(server_handle, princ, new_password);
120 
121 #ifdef ZEROPASSWD
122     if (!ret_pw)
123         memset(buffer, 0, sizeof(buffer)); /* in case we read a new password */
124 #endif
125 
126     if (code == KADM5_OK) {
127         strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_CHANGED), msg_len - 1);
128         msg_ret[msg_len - 1] = '\0';
129         return(0);
130     }
131 
132     if ((code != KADM5_PASS_Q_TOOSHORT) &&
133         (code != KADM5_PASS_REUSE) &&(code != KADM5_PASS_Q_CLASS) &&
134         (code != KADM5_PASS_Q_DICT) && (code != KADM5_PASS_TOOSOON)) {
135         /* Can't get more info for other errors */
136         snprintf(msg_ret, msg_len, "%s\n%s %s\n",
137                  string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
138                  error_message(code),
139                  string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
140         return(code);
141     }
142 
143     /* Ok, we have a password quality error. Return a good message */
144 
145     if (code == KADM5_PASS_REUSE) {
146         strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_REUSE), msg_len - 1);
147         msg_ret[msg_len - 1] = '\0';
148         return(code);
149     }
150 
151     if (code == KADM5_PASS_Q_DICT) {
152         strncpy(msg_ret, string_text(CHPASS_UTIL_PASSWORD_IN_DICTIONARY),
153                 msg_len - 1);
154         msg_ret[msg_len - 1] = '\0';
155         return(code);
156     }
157 
158     /* Look up policy for the remaining messages */
159 
160     code2 = kadm5_get_principal (lhandle, princ, &princ_ent,
161                                  KADM5_PRINCIPAL_NORMAL_MASK);
162     if (code2 != 0) {
163         snprintf(msg_ret, msg_len, "%s %s\n%s %s\n\n%s\n",
164                  error_message(code2),
165                  string_text(CHPASS_UTIL_GET_PRINC_INFO),
166                  error_message(code),
167                  string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
168                  string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED));
169         msg_ret[msg_len - 1] = '\0';
170         return(code);
171     }
172 
173     if ((princ_ent.aux_attributes & KADM5_POLICY) == 0) {
174         /* Some module implements its own password policy. */
175         snprintf(msg_ret, msg_len, "%s\n\n%s",
176                  error_message(code),
177                  string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED));
178         msg_ret[msg_len - 1] = '\0';
179         (void) kadm5_free_principal_ent(lhandle, &princ_ent);
180         return(code);
181     }
182 
183     code2 = kadm5_get_policy(lhandle, princ_ent.policy,
184                              &policy_ent);
185     if (code2 != 0) {
186         snprintf(msg_ret, msg_len, "%s %s\n%s %s\n\n%s\n ", error_message(code2),
187                  string_text(CHPASS_UTIL_GET_POLICY_INFO),
188                  error_message(code),
189                  string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE),
190                  string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED));
191         (void) kadm5_free_principal_ent(lhandle, &princ_ent);
192         return(code);
193     }
194 
195     if (code == KADM5_PASS_Q_TOOSHORT) {
196         snprintf(msg_ret, msg_len, string_text(CHPASS_UTIL_PASSWORD_TOO_SHORT),
197                  policy_ent.pw_min_length);
198         (void) kadm5_free_principal_ent(lhandle, &princ_ent);
199         (void) kadm5_free_policy_ent(lhandle, &policy_ent);
200         return(code);
201     }
202 
203 /* Can't get more info for other errors */
204 
205     if (code == KADM5_PASS_Q_CLASS) {
206         snprintf(msg_ret, msg_len, string_text(CHPASS_UTIL_TOO_FEW_CLASSES),
207                  policy_ent.pw_min_classes);
208         (void) kadm5_free_principal_ent(lhandle, &princ_ent);
209         (void) kadm5_free_policy_ent(lhandle, &policy_ent);
210         return(code);
211     }
212 
213     if (code == KADM5_PASS_TOOSOON) {
214         time_t until;
215         char *time_string, *ptr;
216 
217         until = ts_incr(princ_ent.last_pwd_change, policy_ent.pw_min_life);
218 
219         time_string = ctime(&until);
220         if (time_string == NULL)
221             time_string = "(error)";
222         else if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
223             *ptr = '\0';
224 
225         snprintf(msg_ret, msg_len, string_text(CHPASS_UTIL_PASSWORD_TOO_SOON),
226                  time_string);
227         (void) kadm5_free_principal_ent(lhandle, &princ_ent);
228         (void) kadm5_free_policy_ent(lhandle, &policy_ent);
229         return(code);
230     }
231 
232     /* We should never get here, but just in case ... */
233     snprintf(msg_ret, msg_len, "%s\n%s %s\n",
234              string_text(CHPASS_UTIL_PASSWORD_NOT_CHANGED),
235              error_message(code),
236              string_text(CHPASS_UTIL_WHILE_TRYING_TO_CHANGE));
237     (void) kadm5_free_principal_ent(lhandle, &princ_ent);
238     (void) kadm5_free_policy_ent(lhandle, &policy_ent);
239     return(code);
240 }
241