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