Merge from vendor branch BINUTILS:
[dragonfly.git] / contrib / libpam / libpam / pam_item.c
1 /* pam_item.c */
2
3 /*
4  * $Id: pam_item.c,v 1.8 1997/02/15 15:58:49 morgan Exp morgan $
5  * $FreeBSD: src/contrib/libpam/libpam/pam_item.c,v 1.1.1.1.6.2 2001/06/11 15:28:12 markm Exp $
6  * $DragonFly: src/contrib/libpam/libpam/Attic/pam_item.c,v 1.2 2003/06/17 04:24:03 dillon Exp $
7  *
8  * $Log: pam_item.c,v $
9  */
10
11 #include <ctype.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <syslog.h>
15
16 #include "pam_private.h"
17
18 #define RESET(X, Y)                    \
19 {                                      \
20     char *_TMP_ = (X);                 \
21     if (_TMP_ != (Y)) {                \
22          (X) = (Y) ? _pam_strdup(Y) : NULL; \
23          if (_TMP_)                    \
24               free(_TMP_);             \
25     }                                  \
26 }
27
28 /* functions */
29
30 int pam_set_item (
31     pam_handle_t *pamh,
32     int item_type,
33     const void *item)
34 {
35     int retval;
36
37     D(("called"));
38
39     IF_NO_PAMH("pam_set_item", pamh, PAM_SYSTEM_ERR);
40     
41     retval = PAM_SUCCESS;
42
43     switch (item_type) {
44     case PAM_SERVICE:
45         /* Setting handlers_loaded to 0 will cause the handlers
46          * to be reloaded on the next call to a service module.
47          */
48         pamh->handlers.handlers_loaded = 0;
49         RESET(pamh->service_name, item);
50         {
51             char *tmp;
52             for (tmp=pamh->service_name; *tmp; ++tmp)
53                 *tmp = tolower(*tmp);                 /* require lower case */
54         }
55         break;
56     case PAM_USER:
57         RESET(pamh->user, item);
58         break;
59     case PAM_USER_PROMPT:
60         RESET(pamh->prompt, item);
61         break;
62     case PAM_TTY:
63         D(("setting tty to %s", item));
64         RESET(pamh->tty, item);
65         break;
66     case PAM_RUSER:
67         RESET(pamh->ruser, item);
68         break;
69     case PAM_RHOST:
70         RESET(pamh->rhost, item);
71         break;
72     case PAM_AUTHTOK:
73          /*
74           * The man page says this is only supposed to be available to
75           * the module providers.  In order to use this item the app
76           * has to #include <security/pam_modules.h>. This is something
77           * it is *not* supposed to do with "Linux-"PAM!  - AGM.
78           */
79     {
80         char *_TMP_ = pamh->authtok;
81         if (_TMP_ == item)            /* not changed so leave alone */
82              break;
83         pamh->authtok = (item) ? _pam_strdup(item) : NULL;
84         if (_TMP_) {
85             _pam_overwrite(_TMP_);
86             free(_TMP_);
87         }
88         break;
89     }
90     case PAM_OLDAUTHTOK:
91          /* See note above. */
92     {
93         char *_TMP_ = pamh->oldauthtok;
94         if (_TMP_ == item)            /* not changed so leave alone */
95              break;
96         pamh->oldauthtok = (item) ? _pam_strdup(item) : NULL;
97         if (_TMP_) {
98             _pam_overwrite(_TMP_);
99             free(_TMP_);
100         }
101         break;
102     }
103     case PAM_CONV:              /* want to change the conversation function */
104         if (item == NULL) {
105             pam_system_log(pamh, NULL, LOG_ERR,
106                            "pam_set_item: attempt to set conv() to NULL");
107             retval = PAM_PERM_DENIED;
108         } else {
109             struct pam_conv *tconv;
110             
111             if ((tconv=
112                  (struct pam_conv *) malloc(sizeof(struct pam_conv))
113                 ) == NULL) {
114                 pam_system_log(pamh, NULL, LOG_CRIT,
115                                "pam_set_item: malloc failed for pam_conv");
116                 retval = PAM_BUF_ERR;
117             } else {
118                 memcpy(tconv, item, sizeof(struct pam_conv));
119                 _pam_drop(pamh->pam_conversation);
120                 pamh->pam_conversation = tconv;
121             }
122         }
123         break;
124     case PAM_FAIL_DELAY:
125         pamh->fail_delay.delay_fn_ptr = item;
126         break;
127     case PAM_LOG_STATE:
128     {
129         char *old_ident = pamh->pam_default_log.ident;
130
131         if (item == NULL) {
132             /* reset the default state */
133             pamh->pam_default_log.ident = x_strdup(PAM_LOG_STATE_IDENT);
134             pamh->pam_default_log.option = PAM_LOG_STATE_OPTION;
135             pamh->pam_default_log.facility = PAM_LOG_STATE_FACILITY;
136         } else {
137             const struct pam_log_state *state = item;
138
139             pamh->pam_default_log.ident = x_strdup(state->ident);
140             pamh->pam_default_log.option = state->option;
141             pamh->pam_default_log.facility = state->facility;
142         }
143         _pam_overwrite(old_ident);
144         _pam_drop(old_ident);
145
146         break;
147     }
148     default:
149         retval = PAM_BAD_ITEM;
150     }
151
152     return (retval);
153 }
154
155 int pam_get_item (
156     const pam_handle_t *pamh,
157     int item_type,
158     const void **item)
159 {
160     D(("called."));
161     IF_NO_PAMH("pam_get_item",pamh,PAM_SYSTEM_ERR);
162
163     if (item == NULL) {
164         pam_system_log(pamh, NULL, LOG_ERR,
165                        "pam_get_item: nowhere to place requested item");
166         return PAM_PERM_DENIED;
167     }
168
169     switch (item_type) {
170     case PAM_SERVICE:
171         *item = pamh->service_name;
172         break;
173     case PAM_USER:
174         *item = pamh->user;
175         break;
176     case PAM_USER_PROMPT:
177         *item = pamh->prompt;
178         break;
179     case PAM_TTY:
180         D(("returning tty=%s", pamh->tty));
181         *item = pamh->tty;
182         break;
183     case PAM_RUSER:
184         *item = pamh->ruser;
185         break;
186     case PAM_RHOST:
187         *item = pamh->rhost;
188         break;
189     case PAM_AUTHTOK:
190         *item = pamh->authtok;
191         break;
192     case PAM_OLDAUTHTOK:
193         *item = pamh->oldauthtok;
194         break;
195     case PAM_CONV:
196         *item = pamh->pam_conversation;
197         break;
198     case PAM_FAIL_DELAY:
199         *item = pamh->fail_delay.delay_fn_ptr;
200         break;
201     case PAM_LOG_STATE:
202         *item = &(pamh->pam_default_log);
203         break;
204     default:
205         /* XXX - I made this up */
206         return PAM_BAD_ITEM;
207     }
208   
209     return PAM_SUCCESS;
210 }
211
212 /* added by AGM 1996/3/2 */
213
214 int pam_get_user(pam_handle_t *pamh, const char **user, const char *prompt)
215 {
216     const char *use_prompt;
217     int retval;
218     struct pam_message msg,*pmsg;
219     struct pam_response *resp;
220
221     D(("called."));
222     IF_NO_PAMH("pam_get_user", pamh, PAM_SYSTEM_ERR);
223
224     if (pamh->pam_conversation == NULL) {
225         pam_system_log(pamh, NULL, LOG_ERR,
226                        "pam_get_user: no conv element in pamh");
227         return PAM_SERVICE_ERR;
228     }
229
230     if (user == NULL) {  /* ensure the the module has suplied a destination */
231         pam_system_log(pamh, NULL, LOG_ERR,
232                        "pam_get_user: nowhere to record username");
233         return PAM_PERM_DENIED;
234     } else
235         *user = NULL;
236     
237     if (pamh->user) {    /* have one so return it */
238         *user = pamh->user;
239         return PAM_SUCCESS;
240     }
241
242     /* will need a prompt */
243     use_prompt = prompt;
244     if (use_prompt == NULL) {
245         use_prompt = pamh->prompt;
246         if (use_prompt == NULL) {
247             use_prompt = PAM_DEFAULT_PROMPT;
248         }
249     }
250
251     /* If we are resuming an old conversation, we verify that the prompt
252        is the same.  Anything else is an error. */
253     if (pamh->former.want_user) {
254         /* must have a prompt to resume with */
255         if (! pamh->former.prompt) {
256                     pam_system_log(pamh, NULL, LOG_ERR,
257                                    "pam_get_user: failed to resume with prompt"
258                         );
259             return PAM_ABORT;
260         }
261
262         /* must be the same prompt as last time */
263         if (strcmp(pamh->former.prompt, use_prompt)) {
264             pam_system_log(pamh, NULL, LOG_ERR,
265                            "pam_get_user: resumed with different prompt");
266             return PAM_ABORT;
267         }
268
269         /* ok, we can resume where we left off last time */
270         pamh->former.want_user = PAM_FALSE;
271         _pam_overwrite(pamh->former.prompt);
272         _pam_drop(pamh->former.prompt);
273     }
274
275     /* converse with application -- prompt user for a username */
276     pmsg = &msg;
277     msg.msg_style = PAM_PROMPT_ECHO_ON;
278     msg.msg = use_prompt;
279     resp = NULL;
280
281     retval = pamh->pam_conversation->
282         conv(1, (const struct pam_message **) &pmsg, &resp,
283              pamh->pam_conversation->appdata_ptr);
284
285     if (retval == PAM_CONV_AGAIN) {
286         /* conversation function is waiting for an event - save state */
287         D(("conversation function is not ready yet"));
288         pamh->former.want_user = PAM_TRUE;
289         pamh->former.prompt = _pam_strdup(use_prompt);
290     } else if (resp == NULL) {
291         /*
292          * conversation should have given a response
293          */
294         D(("pam_get_user: no response provided"));
295         retval = PAM_CONV_ERR;
296     } else if (retval == PAM_SUCCESS) {            /* copy the username */
297         /*
298          * now we set the PAM_USER item -- this was missing from pre.53
299          * releases. However, reading the Sun manual, it is part of
300          * the standard API.
301          */
302         RESET(pamh->user, resp->resp);
303         *user = pamh->user;
304     }
305
306     if (resp) {
307         /*
308          * note 'resp' is allocated by the application and is
309          * correctly free()'d here
310          */
311         _pam_drop_reply(resp, 1);
312     }
313
314     return retval;        /* pass on any error from conversation */
315 }