364 lines
7.8 KiB
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
|