pam-eid/pam_eid.c

364 lines
7.8 KiB
C

/*
This software is licensed under GPL-3, as shown in the file LICENSE
Author: Linux Gruppe IRB
Copyright: Linux Gruppe IRB, 2024
*/
#include <curl/curl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <syslog.h>
/* We have to make this definitions before we include the pam header files! */
#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#define PAM_SM_PASSWORD
#include <security/pam_modutil.h>
#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include "pam_eid.h"
#include "auth.h"
#define PACKAGE "pam_eid"
struct module_data {
int pamResult;
CURL *curl;
};
Params params;
static int parse_args(pam_handle_t *pamh, int argc, const char **argv)
{
params.echo_pin = PAM_PROMPT_ECHO_OFF;
params.debug = 0;
for (int i = 0; i < argc; i++)
{
if (!strcmp(argv[i], "debug"))
params.debug = 1;
else if (!strcmp(argv[i], "echo_pin"))
params.echo_pin = PAM_PROMPT_ECHO_ON;
else
{
pam_syslog(pamh, LOG_ERR, "Unrecognized option \"%s\"", argv[i]);
return PAM_AUTH_ERR;
}
}
return PAM_SUCCESS;
}
void module_data_cleanup(pam_handle_t *pamh, void *data, int error_status)
{
struct module_data *module_data = data;
if (module_data != NULL) {
if (module_data->curl)
curl_easy_cleanup(module_data->curl);
free(module_data);
}
}
static int module_initialize(pam_handle_t *pamh, int flags, int argc,
const char **argv,
struct module_data **module_data,
int pamResult)
{
struct module_data *data = calloc(1, sizeof *data);
int r;
if (data == NULL) {
pam_syslog(pamh, LOG_CRIT, "calloc() failed: %s",
strerror(errno));
r = PAM_BUF_ERR;
goto err;
}
data->pamResult = pamResult;
data->curl = curl_easy_init();
if (data->curl == NULL) {
pam_syslog(pamh, LOG_ERR, "curl_easy_init() failed");
r = PAM_SERVICE_ERR;
goto err;
}
r = pam_set_data(pamh, PACKAGE, data, module_data_cleanup);
if (r != PAM_SUCCESS)
{
pam_syslog(pamh, LOG_ERR, "pam_set_data() failed");
goto err;
}
*module_data = data;
data = NULL;
err:
module_data_cleanup(pamh, data, r);
return r;
}
static int module_refresh(pam_handle_t *pamh, int flags, int argc,
const char **argv, const char **user,
CURL **curl, int pamResult)
{
struct module_data *module_data;
int r;
if ((pam_get_data(pamh, PACKAGE, (void *)&module_data) != PAM_SUCCESS) ||
(module_data == NULL))
{
r = module_initialize(pamh, flags, argc, argv, &module_data, pamResult);
if (r != PAM_SUCCESS)
goto err;
}
r = pam_get_user(pamh, user, NULL);
if (r != PAM_SUCCESS)
{
pam_syslog(pamh, LOG_ERR, "pam_get_user() failed %s",
pam_strerror(pamh, r));
goto err;
}
*curl = module_data->curl;
err:
return r;
}
int pam_sm_authenticate(pam_handle_t *pamh,
int flags,
int argc,
const char **argv)
{
PAM_MODUTIL_DEF_PRIVS(privs);
const char *authtok, *user;
struct passwd *passwd;
CURL *curl;
pid_t pid;
int r, s;
r = parse_args(pamh, argc, argv);
if (r != PAM_SUCCESS)
goto err;
r = module_refresh(pamh, flags, argc, argv, &user, &curl, PAM_AUTH_ERR);
if (r != PAM_SUCCESS)
goto err;
if (params.debug)
pam_syslog(pamh, LOG_DEBUG, "Trying to authenticate user %s", user);
if ((passwd = getpwnam(user)) == NULL)
{
pam_syslog(pamh, LOG_ERR, "getpwnam() failed: %s",
strerror(errno));
r = PAM_USER_UNKNOWN;
goto err;
}
if ((r = pam_get_authtok(pamh, PAM_AUTHTOK, &authtok, "Password: ")) != PAM_SUCCESS)
{
pam_syslog(pamh, LOG_ERR, "pam_get_authtok() failed");
goto err;
}
if ((r = pam_modutil_drop_priv(pamh, &privs, passwd)) != PAM_SUCCESS)
{
pam_syslog(pamh, LOG_ERR, "pam_modutil_drop_priv() failed: %s",
pam_strerror(pamh, r));
goto err;
}
pid = fork();
switch (pid)
{
case -1:
pam_syslog(pamh, LOG_ERR, "fork() failed: %s", strerror(errno));
r = PAM_SERVICE_ERR;
break;
case 0:
{
char *envp[3];
DIR *dir;
if ((envp[0] = (char *) malloc(sizeof("AUSWEISAPP2_AUTOMATIC_PIN=123456"))) == NULL)
{
pam_syslog(pamh, LOG_CRIT, "malloc() failed: strerror(errno)");
exit(errno);
}
if ((envp[1] = (char *) malloc(sizeof("XDG_RUNTIME_DIR=/run/user/12345"))) == NULL)
{
pam_syslog(pamh, LOG_CRIT, "malloc() failed: strerror(errno)");
exit(errno);
}
sprintf(envp[0], "AUSWEISAPP2_AUTOMATIC_PIN=%s", authtok);
sprintf(envp[1], "XDG_RUNTIME_DIR=/run/user/%d", (int) passwd->pw_uid);
envp[2] = (char *) NULL;
if ((dir = opendir(strstr(envp[1], "/"))) == NULL)
if (errno == ENOENT)
{
mkdir(strstr(envp[1], "/"), 0700);
if (chown(strstr(envp[1], "/"), passwd->pw_uid, passwd->pw_gid) != 0)
if (params.debug)
pam_syslog(pamh, LOG_DEBUG, "chown() failed: %s", strerror(errno));
}
if (params.debug)
pam_syslog(pamh, LOG_DEBUG, "Original UID is %d, original GID is %d", (int) getuid(), (int) getgid());
if (setgid(passwd->pw_gid) != 0)
{
pam_syslog(pamh, LOG_ERR, "setgid() failed: %s",
strerror(errno));
exit(errno);
}
if (setuid(passwd->pw_uid) != 0)
{
pam_syslog(pamh, LOG_ERR, "setuid() failed: %s",
strerror(errno));
exit(errno);
}
if (params.debug)
pam_syslog(pamh, LOG_DEBUG, "New UID is %d, new GID is %d", (int) getuid(), (int) getgid());
if ((execle("/usr/bin/AusweisApp", "AusweisApp", "--ui", "websocket",
"--ui", "webservice", "--ui", "automatic", "-platform",
"offscreen", "--no-loghandler", "--no-logfile", "--port",
"41325", (char *) NULL, envp)) == -1)
{
pam_syslog(pamh, LOG_ERR, "execle() failed: %s", strerror(errno));
exit(errno);
}
break;
}
default:
r = auth(pamh, passwd, curl);
kill(pid, SIGKILL);
waitpid(pid, NULL, 0);
break;
}
err:
if ((s = pam_modutil_regain_priv(pamh, &privs)) != PAM_SUCCESS)
{
pam_syslog(pamh, LOG_ERR, "pam_modutil_regain_priv() failed: %s",
pam_strerror(pamh, r));
if (r == PAM_SUCCESS)
r = s;
}
if (params.debug)
{
if (r == PAM_SUCCESS)
pam_syslog(pamh, LOG_DEBUG, "User %s was authenticated successfully", user);
else
pam_syslog(pamh, LOG_DEBUG, "User %s could not be authenticated", user);
}
module_refresh(pamh, flags, argc, argv, &user, &curl, r);
return r;
}
int pam_sm_setcred(pam_handle_t *pamh,
int flags,
int argc,
const char **argv)
{
struct module_data moduleData;
int r;
if ((r = pam_get_data(pamh, PACKAGE, (void *) &moduleData)) != PAM_SUCCESS)
{
pam_syslog(pamh, LOG_ERR, "pam_get_data() failed: %s", pam_strerror(pamh, r));
return r;
}
return moduleData.pamResult;
}
int pam_sm_acct_mgmt(pam_handle_t * pamh,
int flags,
int argc,
const char **argv)
{
return PAM_SUCCESS;
}
int pam_sm_open_session(pam_handle_t * pamh,
int flags,
int argc,
const char **argv)
{
pam_syslog(pamh, LOG_ERR,
"Function pam_sm_open_session() is not implemented "
"in this module");
return PAM_SERVICE_ERR;
}
int pam_sm_close_session(pam_handle_t * pamh,
int flags,
int argc,
const char **argv)
{
pam_syslog(pamh, LOG_ERR,
"Function pam_sm_close_session() is not implemented "
"in this module");
return PAM_SERVICE_ERR;
}
int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
const char **argv)
{
pam_syslog(pamh, LOG_ERR,
"Function pam_sm_chauthtok() is not implemented "
"in this module");
return PAM_SERVICE_ERR;
}
#ifdef PAM_STATIC
/* static module data */
struct pam_module _pam_group_modstruct = {
PACKAGE,
pam_sm_authenticate,
pam_sm_setcred,
pam_sm_acct_mgmt,
pam_sm_open_session,
pam_sm_close_session,
pam_sm_chauthtok
};
#endif