xref: /freebsd/contrib/pam-krb5/tests/fakepam/data.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
1 /*
2  * Data manipulation functions for the fake PAM library, used for testing.
3  *
4  * This file contains the implementation of pam_get_* and pam_set_* for the
5  * various data items supported by the PAM library, plus the PAM environment
6  * manipulation functions.
7  *
8  * The canonical version of this file is maintained in the rra-c-util package,
9  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
10  *
11  * Written by Russ Allbery <eagle@eyrie.org>
12  * Copyright 2017, 2020 Russ Allbery <eagle@eyrie.org>
13  * Copyright 2010-2011, 2014
14  *     The Board of Trustees of the Leland Stanford Junior University
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining a
17  * copy of this software and associated documentation files (the "Software"),
18  * to deal in the Software without restriction, including without limitation
19  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20  * and/or sell copies of the Software, and to permit persons to whom the
21  * Software is furnished to do so, subject to the following conditions:
22  *
23  * The above copyright notice and this permission notice shall be included in
24  * all copies or substantial portions of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
29  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32  * DEALINGS IN THE SOFTWARE.
33  *
34  * SPDX-License-Identifier: MIT
35  */
36 
37 #include <config.h>
38 #include <portable/pam.h>
39 #include <portable/system.h>
40 
41 #include <tests/fakepam/pam.h>
42 
43 /* Used for unused parameters to silence gcc warnings. */
44 #define UNUSED __attribute__((__unused__))
45 
46 
47 /*
48  * Return a stored PAM data element in the provided data variable.  As a
49  * special case, if the data is NULL, pretend it doesn't exist.
50  */
51 int
pam_get_data(const pam_handle_t * pamh,const char * name,const void ** data)52 pam_get_data(const pam_handle_t *pamh, const char *name, const void **data)
53 {
54     struct fakepam_data *item;
55 
56     for (item = pamh->data; item != NULL; item = item->next)
57         if (strcmp(item->name, name) == 0) {
58             if (item->data == NULL)
59                 return PAM_NO_MODULE_DATA;
60             *data = item->data;
61             return PAM_SUCCESS;
62         }
63     return PAM_NO_MODULE_DATA;
64 }
65 
66 
67 /*
68  * Store a data item.  Replaces the existing data item (calling its cleanup)
69  * if it is already set; otherwise, add a new data item.
70  */
71 int
pam_set_data(pam_handle_t * pamh,const char * item,void * data,void (* cleanup)(pam_handle_t *,void *,int))72 pam_set_data(pam_handle_t *pamh, const char *item, void *data,
73              void (*cleanup)(pam_handle_t *, void *, int))
74 {
75     struct fakepam_data *p;
76 
77     for (p = pamh->data; p != NULL; p = p->next)
78         if (strcmp(p->name, item) == 0) {
79             if (p->cleanup != NULL)
80                 p->cleanup(pamh, p->data, PAM_DATA_REPLACE);
81             p->data = data;
82             p->cleanup = cleanup;
83             return PAM_SUCCESS;
84         }
85     p = malloc(sizeof(struct fakepam_data));
86     if (p == NULL)
87         return PAM_BUF_ERR;
88     p->name = strdup(item);
89     if (p->name == NULL) {
90         free(p);
91         return PAM_BUF_ERR;
92     }
93     p->data = data;
94     p->cleanup = cleanup;
95     p->next = pamh->data;
96     pamh->data = p;
97     return PAM_SUCCESS;
98 }
99 
100 
101 /*
102  * Retrieve a PAM item.  Currently, this only supports a limited subset of the
103  * possible items.
104  */
105 int
pam_get_item(const pam_handle_t * pamh,int item,PAM_CONST void ** data)106 pam_get_item(const pam_handle_t *pamh, int item, PAM_CONST void **data)
107 {
108     switch (item) {
109     case PAM_AUTHTOK:
110         *data = pamh->authtok;
111         return PAM_SUCCESS;
112     case PAM_CONV:
113         if (pamh->conversation) {
114             *data = pamh->conversation;
115             return PAM_SUCCESS;
116         } else {
117             return PAM_BAD_ITEM;
118         }
119     case PAM_OLDAUTHTOK:
120         *data = pamh->oldauthtok;
121         return PAM_SUCCESS;
122     case PAM_RHOST:
123         *data = (PAM_CONST char *) pamh->rhost;
124         return PAM_SUCCESS;
125     case PAM_RUSER:
126         *data = (PAM_CONST char *) pamh->ruser;
127         return PAM_SUCCESS;
128     case PAM_SERVICE:
129         *data = (PAM_CONST char *) pamh->service;
130         return PAM_SUCCESS;
131     case PAM_TTY:
132         *data = (PAM_CONST char *) pamh->tty;
133         return PAM_SUCCESS;
134     case PAM_USER:
135         *data = (PAM_CONST char *) pamh->user;
136         return PAM_SUCCESS;
137     case PAM_USER_PROMPT:
138         *data = "login: ";
139         return PAM_SUCCESS;
140     default:
141         return PAM_BAD_ITEM;
142     }
143 }
144 
145 
146 /*
147  * Set a PAM item.  Currently only PAM_USER is supported.
148  */
149 int
pam_set_item(pam_handle_t * pamh,int item,PAM_CONST void * data)150 pam_set_item(pam_handle_t *pamh, int item, PAM_CONST void *data)
151 {
152     switch (item) {
153     case PAM_AUTHTOK:
154         free(pamh->authtok);
155         pamh->authtok = strdup(data);
156         if (pamh->authtok == NULL)
157             return PAM_BUF_ERR;
158         return PAM_SUCCESS;
159     case PAM_OLDAUTHTOK:
160         free(pamh->oldauthtok);
161         pamh->oldauthtok = strdup(data);
162         if (pamh->oldauthtok == NULL)
163             return PAM_BUF_ERR;
164         return PAM_SUCCESS;
165     case PAM_RHOST:
166         free(pamh->rhost);
167         pamh->rhost = strdup(data);
168         if (pamh->rhost == NULL)
169             return PAM_BUF_ERR;
170         return PAM_SUCCESS;
171     case PAM_RUSER:
172         free(pamh->ruser);
173         pamh->ruser = strdup(data);
174         if (pamh->ruser == NULL)
175             return PAM_BUF_ERR;
176         return PAM_SUCCESS;
177     case PAM_TTY:
178         free(pamh->tty);
179         pamh->tty = strdup(data);
180         if (pamh->tty == NULL)
181             return PAM_BUF_ERR;
182         return PAM_SUCCESS;
183     case PAM_USER:
184         pamh->user = (const char *) data;
185         return PAM_SUCCESS;
186     default:
187         return PAM_BAD_ITEM;
188     }
189 }
190 
191 
192 /*
193  * Return the user for the PAM context.
194  */
195 int
pam_get_user(pam_handle_t * pamh,PAM_CONST char ** user,const char * prompt UNUSED)196 pam_get_user(pam_handle_t *pamh, PAM_CONST char **user,
197              const char *prompt UNUSED)
198 {
199     if (pamh->user == NULL)
200         return PAM_CONV_ERR;
201     else {
202         *user = (PAM_CONST char *) pamh->user;
203         return PAM_SUCCESS;
204     }
205 }
206 
207 
208 /*
209  * Return a setting in the PAM environment.
210  */
211 PAM_CONST char *
pam_getenv(pam_handle_t * pamh,const char * name)212 pam_getenv(pam_handle_t *pamh, const char *name)
213 {
214     size_t i;
215 
216     if (pamh->environ == NULL)
217         return NULL;
218     for (i = 0; pamh->environ[i] != NULL; i++)
219         if (strncmp(name, pamh->environ[i], strlen(name)) == 0
220             && pamh->environ[i][strlen(name)] == '=')
221             return pamh->environ[i] + strlen(name) + 1;
222     return NULL;
223 }
224 
225 
226 /*
227  * Return a newly malloc'd copy of the complete PAM environment.  This must be
228  * freed by the caller.
229  */
230 char **
pam_getenvlist(pam_handle_t * pamh)231 pam_getenvlist(pam_handle_t *pamh)
232 {
233     char **env;
234     size_t i;
235 
236     if (pamh->environ == NULL) {
237         pamh->environ = malloc(sizeof(char *));
238         if (pamh->environ == NULL)
239             return NULL;
240         pamh->environ[0] = NULL;
241     }
242     for (i = 0; pamh->environ[i] != NULL; i++)
243         ;
244     env = calloc(i + 1, sizeof(char *));
245     if (env == NULL)
246         return NULL;
247     for (i = 0; pamh->environ[i] != NULL; i++) {
248         env[i] = strdup(pamh->environ[i]);
249         if (env[i] == NULL)
250             goto fail;
251     }
252     env[i] = NULL;
253     return env;
254 
255 fail:
256     for (i = 0; env[i] != NULL; i++)
257         free(env[i]);
258     free(env);
259     return NULL;
260 }
261 
262 
263 /*
264  * Add a setting to the PAM environment.  If there is another existing
265  * variable with the same value, the value is replaced, unless the setting
266  * doesn't end in an equal sign.  If it doesn't end in an equal sign, any
267  * existing environment variable of that name is removed.  This follows the
268  * Linux PAM semantics.
269  *
270  * On HP-UX, there is no separate PAM environment, so the module just uses the
271  * main environment.  For our tests to work on that platform, we therefore
272  * have to do the same thing.
273  */
274 #ifdef HAVE_PAM_GETENV
275 int
pam_putenv(pam_handle_t * pamh,const char * setting)276 pam_putenv(pam_handle_t *pamh, const char *setting)
277 {
278     char *copy = NULL;
279     const char *equals;
280     size_t namelen;
281     bool delete = false;
282     bool found = false;
283     size_t i, j;
284     char **env;
285 
286     equals = strchr(setting, '=');
287     if (equals != NULL)
288         namelen = equals - setting;
289     else {
290         delete = true;
291         namelen = strlen(setting);
292     }
293     if (!delete) {
294         copy = strdup(setting);
295         if (copy == NULL)
296             return PAM_BUF_ERR;
297     }
298 
299     /* Handle the first call to pam_putenv. */
300     if (pamh->environ == NULL) {
301         if (delete)
302             return PAM_BAD_ITEM;
303         pamh->environ = calloc(2, sizeof(char *));
304         if (pamh->environ == NULL) {
305             free(copy);
306             return PAM_BUF_ERR;
307         }
308         pamh->environ[0] = copy;
309         pamh->environ[1] = NULL;
310         return PAM_SUCCESS;
311     }
312 
313     /*
314      * We have an existing array.  See if we're replacing a value, deleting a
315      * value, or adding a new one.  When deleting, waste a bit of memory but
316      * save some time by not bothering to reduce the size of the array.
317      */
318     for (i = 0; pamh->environ[i] != NULL; i++)
319         if (strncmp(setting, pamh->environ[i], namelen) == 0
320             && pamh->environ[i][namelen] == '=') {
321             if (delete) {
322                 free(pamh->environ[i]);
323                 for (j = i + 1; pamh->environ[j] != NULL; i++, j++)
324                     pamh->environ[i] = pamh->environ[j];
325                 pamh->environ[i] = NULL;
326             } else {
327                 free(pamh->environ[i]);
328                 pamh->environ[i] = copy;
329             }
330             found = true;
331             break;
332         }
333     if (!found) {
334         if (delete)
335             return PAM_BAD_ITEM;
336         env = reallocarray(pamh->environ, (i + 2), sizeof(char *));
337         if (env == NULL) {
338             free(copy);
339             return PAM_BUF_ERR;
340         }
341         pamh->environ = env;
342         pamh->environ[i] = copy;
343         pamh->environ[i + 1] = NULL;
344     }
345     return PAM_SUCCESS;
346 }
347 
348 #else /* !HAVE_PAM_GETENV */
349 
350 int
pam_putenv(pam_handle_t * pamh UNUSED,const char * setting)351 pam_putenv(pam_handle_t *pamh UNUSED, const char *setting)
352 {
353     return putenv((char *) setting);
354 }
355 
356 #endif /* !HAVE_PAM_GETENV */
357