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