1 /* pam_item.c */
2
3 /*
4 * $Id$
5 */
6
7 #include "pam_private.h"
8 #include "pam_inline.h"
9
10 #include <ctype.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <syslog.h>
14
15 #define TRY_SET(X, Y) \
16 { \
17 if ((X) != (Y)) { \
18 char *_TMP_ = _pam_strdup(Y); \
19 if (_TMP_ == NULL && (Y) != NULL) \
20 return PAM_BUF_ERR; \
21 free(X); \
22 (X) = _TMP_; \
23 } \
24 }
25
26 /* functions */
27
28 int pam_set_item (pam_handle_t *pamh, int item_type, const void *item)
29 {
30 int retval;
31
32 D(("called"));
33
34 IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR);
35
36 retval = PAM_SUCCESS;
37
38 switch (item_type) {
39
40 case PAM_SERVICE:
41 /* Setting handlers_loaded to 0 will cause the handlers
42 * to be reloaded on the next call to a service module.
43 */
44 pamh->handlers.handlers_loaded = 0;
45 TRY_SET(pamh->service_name, item);
46 {
47 char *tmp;
48 for (tmp=pamh->service_name; *tmp; ++tmp)
49 *tmp = tolower(*tmp); /* require lower case */
50 }
51 break;
52
53 case PAM_USER:
54 TRY_SET(pamh->user, item);
55 pamh->former.fail_user = PAM_SUCCESS;
56 break;
57
58 case PAM_USER_PROMPT:
59 TRY_SET(pamh->prompt, item);
60 pamh->former.fail_user = PAM_SUCCESS;
61 break;
62
63 case PAM_TTY:
64 D(("setting tty to %s", item));
65 TRY_SET(pamh->tty, item);
66 break;
67
68 case PAM_RUSER:
69 TRY_SET(pamh->ruser, item);
70 break;
71
72 case PAM_RHOST:
73 TRY_SET(pamh->rhost, item);
74 break;
75
76 case PAM_AUTHTOK:
77 /*
78 * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
79 * modules.
80 */
81 if (__PAM_FROM_MODULE(pamh)) {
82 if (pamh->authtok != item) {
83 pam_overwrite_string(pamh->authtok);
84 TRY_SET(pamh->authtok, item);
85 }
86 } else {
87 retval = PAM_BAD_ITEM;
88 }
89
90 break;
91
92 case PAM_OLDAUTHTOK:
93 /*
94 * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
95 * modules.
96 */
97 if (__PAM_FROM_MODULE(pamh)) {
98 if (pamh->oldauthtok != item) {
99 pam_overwrite_string(pamh->oldauthtok);
100 TRY_SET(pamh->oldauthtok, item);
101 }
102 } else {
103 retval = PAM_BAD_ITEM;
104 }
105
106 break;
107
108 case PAM_CONV: /* want to change the conversation function */
109 if (item == NULL) {
110 pam_syslog(pamh, LOG_ERR,
111 "pam_set_item: attempt to set conv() to NULL");
112 retval = PAM_PERM_DENIED;
113 } else {
114 struct pam_conv *tconv;
115
116 if ((tconv=
117 (struct pam_conv *) malloc(sizeof(struct pam_conv))
118 ) == NULL) {
119 pam_syslog(pamh, LOG_CRIT,
120 "pam_set_item: malloc failed for pam_conv");
121 retval = PAM_BUF_ERR;
122 } else {
123 memcpy(tconv, item, sizeof(struct pam_conv));
124 _pam_drop(pamh->pam_conversation);
125 pamh->pam_conversation = tconv;
126 pamh->former.fail_user = PAM_SUCCESS;
127 }
128 }
129 break;
130
131 case PAM_FAIL_DELAY:
132 pamh->fail_delay.delay_fn_ptr = item;
133 break;
134
135 case PAM_XDISPLAY:
136 TRY_SET(pamh->xdisplay, item);
137 break;
138
139 case PAM_XAUTHDATA:
140 if (&pamh->xauth == item)
141 break;
142 if (pamh->xauth.namelen) {
143 pam_overwrite_string(pamh->xauth.name);
144 free(pamh->xauth.name);
145 }
146 if (pamh->xauth.datalen) {
147 pam_overwrite_n(pamh->xauth.data, (unsigned int) pamh->xauth.datalen);
148 free(pamh->xauth.data);
149 }
150 pamh->xauth = *((const struct pam_xauth_data *) item);
151 if ((pamh->xauth.name=_pam_strdup(pamh->xauth.name)) == NULL) {
152 pam_overwrite_object(&pamh->xauth);
153 return PAM_BUF_ERR;
154 }
155 if ((pamh->xauth.data=_pam_memdup(pamh->xauth.data,
156 pamh->xauth.datalen)) == NULL) {
157 pam_overwrite_string(pamh->xauth.name);
158 free(pamh->xauth.name);
159 pam_overwrite_object(&pamh->xauth);
160 return PAM_BUF_ERR;
161 }
162 break;
163
164 case PAM_AUTHTOK_TYPE:
165 TRY_SET(pamh->authtok_type, item);
166 break;
167
168 default:
169 retval = PAM_BAD_ITEM;
170 }
171
172 return retval;
173 }
174
175 int pam_get_item (const pam_handle_t *pamh, int item_type, const void **item)
176 {
177 int retval = PAM_SUCCESS;
178
179 D(("called."));
180 IF_NO_PAMH("pam_get_item", pamh, PAM_SYSTEM_ERR);
181
182 if (item == NULL) {
183 pam_syslog(pamh, LOG_ERR,
184 "pam_get_item: nowhere to place requested item");
185 return PAM_PERM_DENIED;
186 }
187 else
188 *item = NULL;
189
190 switch (item_type) {
191 case PAM_SERVICE:
192 *item = pamh->service_name;
193 break;
194
195 case PAM_USER:
196 D(("returning user=%s", pamh->user));
197 *item = pamh->user;
198 break;
199
200 case PAM_USER_PROMPT:
201 D(("returning userprompt=%s", pamh->user));
202 *item = pamh->prompt;
203 break;
204
205 case PAM_TTY:
206 D(("returning tty=%s", pamh->tty));
207 *item = pamh->tty;
208 break;
209
210 case PAM_RUSER:
211 *item = pamh->ruser;
212 break;
213
214 case PAM_RHOST:
215 *item = pamh->rhost;
216 break;
217
218 case PAM_AUTHTOK:
219 /*
220 * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
221 * modules.
222 */
223 if (__PAM_FROM_MODULE(pamh)) {
224 *item = pamh->authtok;
225 } else {
226 retval = PAM_BAD_ITEM;
227 }
228 break;
229
230 case PAM_OLDAUTHTOK:
231 /*
232 * PAM_AUTHTOK and PAM_OLDAUTHTOK are only accessible from
233 * modules.
234 */
235 if (__PAM_FROM_MODULE(pamh)) {
236 *item = pamh->oldauthtok;
237 } else {
238 retval = PAM_BAD_ITEM;
239 }
240 break;
241
242 case PAM_CONV:
243 *item = pamh->pam_conversation;
244 break;
245
246 case PAM_FAIL_DELAY:
247 *item = pamh->fail_delay.delay_fn_ptr;
248 break;
249
250 case PAM_XDISPLAY:
251 *item = pamh->xdisplay;
252 break;
253
254 case PAM_XAUTHDATA:
255 *item = &pamh->xauth;
256 break;
257
258 case PAM_AUTHTOK_TYPE:
259 *item = pamh->authtok_type;
260 break;
261
262 default:
263 retval = PAM_BAD_ITEM;
264 }
265
266 return retval;
267 }
268
269 /*
270 * This function is the 'preferred method to obtain the username'.
271 */
272
273 int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt)
274 {
275 const char *use_prompt;
276 int retval;
277 struct pam_message msg;
278 const struct pam_message *pmsg;
279 struct pam_response *resp;
280
281 D(("called."));
282
283 IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR);
284
285 if (user == NULL) {
286 /* ensure that the module has supplied a destination */
287 pam_syslog(pamh, LOG_ERR, "pam_get_user: nowhere to record username");
288 return PAM_SYSTEM_ERR;
289 } else
290 *user = NULL;
291
292 if (pamh->pam_conversation == NULL) {
293 pam_syslog(pamh, LOG_ERR, "pam_get_user: no conv element in pamh");
294 return PAM_SYSTEM_ERR;
295 }
296
297 if (pamh->user) { /* have one so return it */
298 *user = pamh->user;
299 return PAM_SUCCESS;
300 }
301
302 if (pamh->former.fail_user != PAM_SUCCESS)
303 return pamh->former.fail_user;
304
305 /* will need a prompt */
306 if (prompt != NULL)
307 use_prompt = prompt;
308 else if (pamh->prompt != NULL)
309 use_prompt = pamh->prompt;
310 else
311 use_prompt = _("login:");
312
313 /* If we are resuming an old conversation, we verify that the prompt
314 is the same. Anything else is an error. */
315 if (pamh->former.want_user) {
316 /* must have a prompt to resume with */
317 if (! pamh->former.prompt) {
318 pam_syslog(pamh, LOG_ERR,
319 "pam_get_user: failed to resume with prompt"
320 );
321 return PAM_ABORT;
322 }
323
324 /* must be the same prompt as last time */
325 if (strcmp(pamh->former.prompt, use_prompt)) {
326 pam_syslog(pamh, LOG_ERR,
327 "pam_get_user: resumed with different prompt");
328 return PAM_ABORT;
329 }
330
331 /* ok, we can resume where we left off last time */
332 pamh->former.want_user = PAM_FALSE;
333 pam_overwrite_string(pamh->former.prompt);
334 _pam_drop(pamh->former.prompt);
335 }
336
337 /* converse with application -- prompt user for a username */
338 pmsg = &msg;
339 msg.msg_style = PAM_PROMPT_ECHO_ON;
340 msg.msg = use_prompt;
341 resp = NULL;
342
343 retval = pamh->pam_conversation->
344 conv(1, &pmsg, &resp, pamh->pam_conversation->appdata_ptr);
345
346 switch (retval) {
347 case PAM_SUCCESS:
348 case PAM_BUF_ERR:
349 case PAM_CONV_AGAIN:
350 case PAM_CONV_ERR:
351 break;
352 default:
353 retval = PAM_CONV_ERR;
354 }
355
356 switch (retval) {
357 case PAM_CONV_AGAIN:
358 /* conversation function is waiting for an event - save state */
359 D(("conversation function is not ready yet"));
360 pamh->former.want_user = PAM_TRUE;
361 pamh->former.prompt = _pam_strdup(use_prompt);
362 break;
363 case PAM_SUCCESS:
364 if (resp != NULL && resp->resp != NULL) {
365 /*
366 * now we set the PAM_USER item -- this was missing from pre.53
367 * releases. However, reading the Sun manual, it is part of
368 * the standard API.
369 */
370 retval = pam_set_item(pamh, PAM_USER, resp->resp);
371 *user = pamh->user;
372 break;
373 } else {
374 /* conversation should have given a response */
375 D(("pam_get_user: no response provided"));
376 retval = PAM_CONV_ERR;
377 }
378 /* fallthrough */
379 default:
380 pamh->former.fail_user = retval;
381 }
382
383 if (resp) {
384 if (retval != PAM_SUCCESS)
385 pam_syslog(pamh, LOG_WARNING,
386 "unexpected response from failed conversation function");
387 /*
388 * note 'resp' is allocated by the application and is
389 * correctly free()'d here
390 */
391 pam_drop_response(resp, 1);
392 }
393
394 D(("completed"));
395 return retval; /* pass on any error from conversation */
396 }