1 /*
2 * Redistribution and use in source and binary forms, with or without
3 * modification, are permitted provided that the following conditions
4 * are met:
5 * 1. Redistributions of source code must retain the above copyright
6 * notice, and the entire permission notice in its entirety,
7 * including the disclaimer of warranties.
8 * 2. Redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution.
11 * 3. The name of the author may not be used to endorse or promote
12 * products derived from this software without specific prior
13 * written permission.
14 *
15 * ALTERNATIVELY, this product may be distributed under the terms of
16 * the GNU Public License, in which case the provisions of the GPL are
17 * required INSTEAD OF the above restrictions. (This clause is
18 * necessary due to a potential bad interaction between the GPL and
19 * the restrictions contained in a BSD-style copyright.)
20 *
21 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include "config.h"
35 #include "pam_private.h"
36 #include "pam_inline.h"
37
38 #include <security/pam_ext.h>
39
40 #define PROMPT _("Password: ")
41 /* For Translators: "%s" is replaced with "<service>". */
42 #define PROMPT_CURRENT_ARG _("Current %s password: ")
43 #define PROMPT_CURRENT_NOARG _("Current password: ")
44 /* For Translators: "%s" is replaced with "<service>". */
45 #define PROMPT_NEW_ARG _("New %s password: ")
46 #define PROMPT_NEW_NOARG _("New password: ")
47 /* For Translators: "%s" is replaced with "<service>". */
48 #define PROMPT_RETYPE_ARG _("Retype new %s password: ")
49 #define PROMPT_RETYPE_NOARG _("Retype new password: ")
50 #define MISTYPED_PASS _("Sorry, passwords do not match.")
51
52 #define PAM_GETAUTHTOK_NOVERIFY 1
53
54 static const char *
55 get_option (pam_handle_t *pamh, const char *option)
56 {
57 int i;
58 size_t len;
59
60
61 if (option == NULL || pamh == NULL ||
62 pamh->mod_argc == 0 || pamh->mod_argv == NULL)
63 return NULL;
64
65 len = strlen (option);
66
67 for (i = 0; i < pamh->mod_argc; i++)
68 {
69 if (strncmp (option, pamh->mod_argv[i], len) == 0)
70 {
71 if (pamh->mod_argv[i][len] == '=')
72 return &(pamh->mod_argv[i][len+1]);
73 else if (pamh->mod_argv[i][len] == '\0')
74 return "";
75 }
76 }
77 return NULL;
78 }
79
80
81 static int
82 pam_get_authtok_internal (pam_handle_t *pamh, int item,
83 const char **authtok, const char *prompt,
84 unsigned int flags)
85
86 {
87 char *resp[2] = {NULL, NULL};
88 const void *prevauthtok;
89 const char *authtok_type = "";
90 int chpass = 0; /* Password change, ask twice for it */
91 int retval;
92
93 if (authtok == NULL)
94 return PAM_SYSTEM_ERR;
95
96 /* PAM_AUTHTOK in password stack returns new password,
97 which needs to be verified. */
98 if (pamh->choice == PAM_CHAUTHTOK)
99 {
100 if (item == PAM_AUTHTOK)
101 {
102 chpass = 1;
103 if (!(flags & PAM_GETAUTHTOK_NOVERIFY))
104 ++chpass;
105 }
106 authtok_type = get_option (pamh, "authtok_type");
107 if (authtok_type == NULL)
108 {
109 retval = pam_get_item (pamh, PAM_AUTHTOK_TYPE, (const void **)&authtok_type);
110 if (retval != PAM_SUCCESS || authtok_type == NULL)
111 authtok_type = "";
112 }
113 else
114 pam_set_item(pamh, PAM_AUTHTOK_TYPE, authtok_type);
115 }
116
117 retval = pam_get_item (pamh, item, &prevauthtok);
118 if (retval == PAM_SUCCESS && prevauthtok != NULL)
119 {
120 *authtok = prevauthtok;
121 return PAM_SUCCESS;
122 }
123 else if (get_option (pamh, "use_first_pass") ||
124 (chpass && get_option (pamh, "use_authtok")))
125 {
126 if (prevauthtok == NULL)
127 {
128 if (chpass)
129 return PAM_AUTHTOK_ERR;
130 else
131 return PAM_AUTH_ERR;
132 }
133 else
134 return retval;
135 }
136
137 if (prompt != NULL)
138 {
139 retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
140 "%s", prompt);
141 if (retval == PAM_SUCCESS && chpass > 1 && resp[0] != NULL)
142 retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1],
143 _("Retype %s"), prompt);
144 }
145 else if (chpass)
146 {
147 pamh->authtok_verified = 0;
148
149 retval = *authtok_type ?
150 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
151 PROMPT_NEW_ARG, authtok_type) :
152 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
153 "%s", PROMPT_NEW_NOARG);
154 if (retval == PAM_SUCCESS && chpass > 1 && resp[0] != NULL)
155 {
156 retval = *authtok_type ?
157 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1],
158 PROMPT_RETYPE_ARG, authtok_type) :
159 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[1],
160 "%s", PROMPT_RETYPE_NOARG);
161 }
162 }
163 else if (item == PAM_OLDAUTHTOK)
164 {
165 retval = *authtok_type ?
166 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
167 PROMPT_CURRENT_ARG, authtok_type) :
168 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0],
169 "%s", PROMPT_CURRENT_NOARG);
170 }
171 else
172 retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp[0], "%s", PROMPT);
173
174 if (retval != PAM_SUCCESS || resp[0] == NULL ||
175 (chpass > 1 && resp[1] == NULL))
176 {
177 /* We want to abort */
178 pam_overwrite_string (resp[0]);
179 _pam_drop (resp[0]);
180 pam_overwrite_string (resp[1]);
181 _pam_drop (resp[1]);
182 if (chpass)
183 pam_error (pamh, _("Password change has been aborted."));
184 return PAM_AUTHTOK_ERR;
185 }
186
187 if (chpass > 1 && strcmp (resp[0], resp[1]) != 0)
188 {
189 pam_error (pamh, MISTYPED_PASS);
190 pam_overwrite_string (resp[0]);
191 _pam_drop (resp[0]);
192 pam_overwrite_string (resp[1]);
193 _pam_drop (resp[1]);
194 return PAM_TRY_AGAIN;
195 }
196
197 pam_overwrite_string (resp[1]);
198 _pam_drop (resp[1]);
199
200 retval = pam_set_item (pamh, item, resp[0]);
201 pam_overwrite_string (resp[0]);
202 _pam_drop (resp[0]);
203 if (retval != PAM_SUCCESS)
204 return retval;
205
206 if (chpass > 1)
207 pamh->authtok_verified = 1;
208
209 return pam_get_item(pamh, item, (const void **)authtok);
210 }
211
212 int
213 pam_get_authtok (pam_handle_t *pamh, int item, const char **authtok,
214 const char *prompt)
215 {
216 return pam_get_authtok_internal (pamh, item, authtok, prompt, 0);
217 }
218
219
220 int
221 pam_get_authtok_noverify (pam_handle_t *pamh, const char **authtok,
222 const char *prompt)
223 {
224 return pam_get_authtok_internal (pamh, PAM_AUTHTOK, authtok, prompt,
225 PAM_GETAUTHTOK_NOVERIFY);
226 }
227
228 int
229 pam_get_authtok_verify (pam_handle_t *pamh, const char **authtok,
230 const char *prompt)
231 {
232 char *resp = NULL;
233 const char *authtok_type = "";
234 int retval;
235
236 if (authtok == NULL || pamh->choice != PAM_CHAUTHTOK)
237 return PAM_SYSTEM_ERR;
238
239 if (pamh->authtok_verified)
240 return pam_get_item (pamh, PAM_AUTHTOK, (const void **)authtok);
241
242 if (prompt != NULL)
243 {
244 retval = pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
245 _("Retype %s"), prompt);
246 }
247 else
248 {
249 retval = pam_get_item (pamh, PAM_AUTHTOK_TYPE, (const void **)&authtok_type);
250 if (retval != PAM_SUCCESS || authtok_type == NULL)
251 authtok_type = "";
252 retval = *authtok_type ?
253 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
254 PROMPT_RETYPE_ARG, authtok_type) :
255 pam_prompt (pamh, PAM_PROMPT_ECHO_OFF, &resp,
256 "%s", PROMPT_RETYPE_NOARG);
257 }
258
259 if (retval != PAM_SUCCESS || resp == NULL)
260 {
261 /* We want to abort the password change */
262 pam_set_item (pamh, PAM_AUTHTOK, NULL);
263 pam_error (pamh, _("Password change has been aborted."));
264 return PAM_AUTHTOK_ERR;
265 }
266
267 if (strcmp (*authtok, resp) != 0)
268 {
269 pam_set_item (pamh, PAM_AUTHTOK, NULL);
270 pam_error (pamh, MISTYPED_PASS);
271 pam_overwrite_string (resp);
272 _pam_drop (resp);
273 return PAM_TRY_AGAIN;
274 }
275
276 retval = pam_set_item (pamh, PAM_AUTHTOK, resp);
277 pam_overwrite_string (resp);
278 _pam_drop (resp);
279 if (retval != PAM_SUCCESS)
280 return retval;
281
282 pamh->authtok_verified = 1;
283
284 return pam_get_item(pamh, PAM_AUTHTOK, (const void **)authtok);
285 }