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 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 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 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 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 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 * 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 ** 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 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 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