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