/*
* Data manipulation functions for the fake PAM library, used for testing.
*
* This file contains the implementation of pam_get_* and pam_set_* for the
* various data items supported by the PAM library, plus the PAM environment
* manipulation functions.
*
* The canonical version of this file is maintained in the rra-c-util package,
* which can be found at .
*
* Written by Russ Allbery
* Copyright 2017, 2020 Russ Allbery
* Copyright 2010-2011, 2014
* The Board of Trustees of the Leland Stanford Junior University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
*/
#include
#include
#include
#include
/* Used for unused parameters to silence gcc warnings. */
#define UNUSED __attribute__((__unused__))
/*
* Return a stored PAM data element in the provided data variable. As a
* special case, if the data is NULL, pretend it doesn't exist.
*/
int
pam_get_data(const pam_handle_t *pamh, const char *name, const void **data)
{
struct fakepam_data *item;
for (item = pamh->data; item != NULL; item = item->next)
if (strcmp(item->name, name) == 0) {
if (item->data == NULL)
return PAM_NO_MODULE_DATA;
*data = item->data;
return PAM_SUCCESS;
}
return PAM_NO_MODULE_DATA;
}
/*
* Store a data item. Replaces the existing data item (calling its cleanup)
* if it is already set; otherwise, add a new data item.
*/
int
pam_set_data(pam_handle_t *pamh, const char *item, void *data,
void (*cleanup)(pam_handle_t *, void *, int))
{
struct fakepam_data *p;
for (p = pamh->data; p != NULL; p = p->next)
if (strcmp(p->name, item) == 0) {
if (p->cleanup != NULL)
p->cleanup(pamh, p->data, PAM_DATA_REPLACE);
p->data = data;
p->cleanup = cleanup;
return PAM_SUCCESS;
}
p = malloc(sizeof(struct fakepam_data));
if (p == NULL)
return PAM_BUF_ERR;
p->name = strdup(item);
if (p->name == NULL) {
free(p);
return PAM_BUF_ERR;
}
p->data = data;
p->cleanup = cleanup;
p->next = pamh->data;
pamh->data = p;
return PAM_SUCCESS;
}
/*
* Retrieve a PAM item. Currently, this only supports a limited subset of the
* possible items.
*/
int
pam_get_item(const pam_handle_t *pamh, int item, PAM_CONST void **data)
{
switch (item) {
case PAM_AUTHTOK:
*data = pamh->authtok;
return PAM_SUCCESS;
case PAM_CONV:
if (pamh->conversation) {
*data = pamh->conversation;
return PAM_SUCCESS;
} else {
return PAM_BAD_ITEM;
}
case PAM_OLDAUTHTOK:
*data = pamh->oldauthtok;
return PAM_SUCCESS;
case PAM_RHOST:
*data = (PAM_CONST char *) pamh->rhost;
return PAM_SUCCESS;
case PAM_RUSER:
*data = (PAM_CONST char *) pamh->ruser;
return PAM_SUCCESS;
case PAM_SERVICE:
*data = (PAM_CONST char *) pamh->service;
return PAM_SUCCESS;
case PAM_TTY:
*data = (PAM_CONST char *) pamh->tty;
return PAM_SUCCESS;
case PAM_USER:
*data = (PAM_CONST char *) pamh->user;
return PAM_SUCCESS;
case PAM_USER_PROMPT:
*data = "login: ";
return PAM_SUCCESS;
default:
return PAM_BAD_ITEM;
}
}
/*
* Set a PAM item. Currently only PAM_USER is supported.
*/
int
pam_set_item(pam_handle_t *pamh, int item, PAM_CONST void *data)
{
switch (item) {
case PAM_AUTHTOK:
free(pamh->authtok);
pamh->authtok = strdup(data);
if (pamh->authtok == NULL)
return PAM_BUF_ERR;
return PAM_SUCCESS;
case PAM_OLDAUTHTOK:
free(pamh->oldauthtok);
pamh->oldauthtok = strdup(data);
if (pamh->oldauthtok == NULL)
return PAM_BUF_ERR;
return PAM_SUCCESS;
case PAM_RHOST:
free(pamh->rhost);
pamh->rhost = strdup(data);
if (pamh->rhost == NULL)
return PAM_BUF_ERR;
return PAM_SUCCESS;
case PAM_RUSER:
free(pamh->ruser);
pamh->ruser = strdup(data);
if (pamh->ruser == NULL)
return PAM_BUF_ERR;
return PAM_SUCCESS;
case PAM_TTY:
free(pamh->tty);
pamh->tty = strdup(data);
if (pamh->tty == NULL)
return PAM_BUF_ERR;
return PAM_SUCCESS;
case PAM_USER:
pamh->user = (const char *) data;
return PAM_SUCCESS;
default:
return PAM_BAD_ITEM;
}
}
/*
* Return the user for the PAM context.
*/
int
pam_get_user(pam_handle_t *pamh, PAM_CONST char **user,
const char *prompt UNUSED)
{
if (pamh->user == NULL)
return PAM_CONV_ERR;
else {
*user = (PAM_CONST char *) pamh->user;
return PAM_SUCCESS;
}
}
/*
* Return a setting in the PAM environment.
*/
PAM_CONST char *
pam_getenv(pam_handle_t *pamh, const char *name)
{
size_t i;
if (pamh->environ == NULL)
return NULL;
for (i = 0; pamh->environ[i] != NULL; i++)
if (strncmp(name, pamh->environ[i], strlen(name)) == 0
&& pamh->environ[i][strlen(name)] == '=')
return pamh->environ[i] + strlen(name) + 1;
return NULL;
}
/*
* Return a newly malloc'd copy of the complete PAM environment. This must be
* freed by the caller.
*/
char **
pam_getenvlist(pam_handle_t *pamh)
{
char **env;
size_t i;
if (pamh->environ == NULL) {
pamh->environ = malloc(sizeof(char *));
if (pamh->environ == NULL)
return NULL;
pamh->environ[0] = NULL;
}
for (i = 0; pamh->environ[i] != NULL; i++)
;
env = calloc(i + 1, sizeof(char *));
if (env == NULL)
return NULL;
for (i = 0; pamh->environ[i] != NULL; i++) {
env[i] = strdup(pamh->environ[i]);
if (env[i] == NULL)
goto fail;
}
env[i] = NULL;
return env;
fail:
for (i = 0; env[i] != NULL; i++)
free(env[i]);
free(env);
return NULL;
}
/*
* Add a setting to the PAM environment. If there is another existing
* variable with the same value, the value is replaced, unless the setting
* doesn't end in an equal sign. If it doesn't end in an equal sign, any
* existing environment variable of that name is removed. This follows the
* Linux PAM semantics.
*
* On HP-UX, there is no separate PAM environment, so the module just uses the
* main environment. For our tests to work on that platform, we therefore
* have to do the same thing.
*/
#ifdef HAVE_PAM_GETENV
int
pam_putenv(pam_handle_t *pamh, const char *setting)
{
char *copy = NULL;
const char *equals;
size_t namelen;
bool delete = false;
bool found = false;
size_t i, j;
char **env;
equals = strchr(setting, '=');
if (equals != NULL)
namelen = equals - setting;
else {
delete = true;
namelen = strlen(setting);
}
if (!delete) {
copy = strdup(setting);
if (copy == NULL)
return PAM_BUF_ERR;
}
/* Handle the first call to pam_putenv. */
if (pamh->environ == NULL) {
if (delete)
return PAM_BAD_ITEM;
pamh->environ = calloc(2, sizeof(char *));
if (pamh->environ == NULL) {
free(copy);
return PAM_BUF_ERR;
}
pamh->environ[0] = copy;
pamh->environ[1] = NULL;
return PAM_SUCCESS;
}
/*
* We have an existing array. See if we're replacing a value, deleting a
* value, or adding a new one. When deleting, waste a bit of memory but
* save some time by not bothering to reduce the size of the array.
*/
for (i = 0; pamh->environ[i] != NULL; i++)
if (strncmp(setting, pamh->environ[i], namelen) == 0
&& pamh->environ[i][namelen] == '=') {
if (delete) {
free(pamh->environ[i]);
for (j = i + 1; pamh->environ[j] != NULL; i++, j++)
pamh->environ[i] = pamh->environ[j];
pamh->environ[i] = NULL;
} else {
free(pamh->environ[i]);
pamh->environ[i] = copy;
}
found = true;
break;
}
if (!found) {
if (delete)
return PAM_BAD_ITEM;
env = reallocarray(pamh->environ, (i + 2), sizeof(char *));
if (env == NULL) {
free(copy);
return PAM_BUF_ERR;
}
pamh->environ = env;
pamh->environ[i] = copy;
pamh->environ[i + 1] = NULL;
}
return PAM_SUCCESS;
}
#else /* !HAVE_PAM_GETENV */
int
pam_putenv(pam_handle_t *pamh UNUSED, const char *setting)
{
return putenv((char *) setting);
}
#endif /* !HAVE_PAM_GETENV */