У меня есть приложение, работающее от непривилегированного пользователя, но в какой-то момент эта программа должна запустить еще одну в качестве пользователя root, было бы неплохо, если бы я мог повторно использовать настроенный модуль PAM, например, su, sudo, login или кого-либо еще.
Поэтому я пытаюсь написать некоторый код для аутентификации root и запустить эту программу с использованием PAM, как это делает sudo, но я не могу запросить пароль, он должен быть автоматическим. Эта непривилегированная программа в определенное время будет иметь доступ к паролю root.
Попробовал этот пример здесь https://www.netbsd.org/docs/guide/en/chap-pam.html но на pam_authenticate всегда возвращается PAM_AUTH_ERR, я попробовал все сконфигурированные модули на моем Ubuntu 18.04.
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdio.h>
#include <sys/wait.h>
#include <err.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <grp.h>
#include <assert.h>
#include <string>
#include <vector>
int converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data)
{
struct pam_response *aresp;
char buf[PAM_MAX_RESP_SIZE];
int i;
data = data;
if (n <= 0 || n > PAM_MAX_NUM_MSG)
return (PAM_CONV_ERR);
if ((aresp = (struct pam_response *) calloc(n, sizeof *aresp)) == NULL)
return (PAM_BUF_ERR);
for (i = 0; i < n; ++i) {
aresp[i].resp_retcode = 0;
aresp[i].resp = NULL;
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_OFF:
//aresp[i].resp = strdup(getpass(msg[i]->msg));
aresp[i].resp = strdup("mypass");
aresp[i].resp_retcode = 0;
if (aresp[i].resp == NULL)
goto fail;
break;
case PAM_PROMPT_ECHO_ON:
fputs(msg[i]->msg, stderr);
if (fgets(buf, sizeof buf, stdin) == NULL)
goto fail;
aresp[i].resp = strdup(buf);
if (aresp[i].resp == NULL)
goto fail;
break;
case PAM_ERROR_MSG:
fputs(msg[i]->msg, stderr);
if (strlen(msg[i]->msg) > 0 &&
msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
fputc('\n', stderr);
break;
case PAM_TEXT_INFO:
fputs(msg[i]->msg, stdout);
if (strlen(msg[i]->msg) > 0 &&
msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
fputc('\n', stdout);
break;
default:
goto fail;
}
}
*resp = aresp;
return (PAM_SUCCESS);
fail:
for (i = 0; i < n; ++i) {
if (aresp[i].resp != NULL) {
memset(aresp[i].resp, 0, strlen(aresp[i].resp));
free(aresp[i].resp);
}
}
memset(aresp, 0, n * sizeof *aresp);
*resp = NULL;
return (PAM_CONV_ERR);
}
static struct pam_conv conv = {
converse,
//misc_conv,
NULL
};
extern char **environ;
static pam_handle_t *pamh;
static struct pam_conv pamc;
static void
usage(void)
{
fprintf(stderr, "Usage: su [login [args]]\n");
exit(1);
}
int
main(int argc, char *argv[])
{
char hostname[64];
const char *user, *tty;
char **args, **pam_envlist, **pam_env;
struct passwd *pwd;
int o, pam_err, status;
pid_t pid;
while ((o = getopt(argc, argv, "h")) != -1)
switch (o) {
case 'h':
default:
usage();
}
argc -= optind;
argv += optind;
if (argc > 0) {
user = *argv;
--argc;
++argv;
} else {
user = "root";
}
int pam_status = PAM_SUCCESS;
/* initialize PAM */
//pamc.conv = &openpam_ttyconv;
if ((pam_status = pam_start("passwd", user, &conv, &pamh)) != PAM_SUCCESS)
{
assert(false);
}
/* set some items */
gethostname(hostname, sizeof(hostname));
if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
{
assert(false);
}
user = getlogin();
if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS)
{
assert(false);
}
tty = ttyname(STDERR_FILENO);
if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
{
assert(false);
}
/* authenticate the applicant */
pam_err = pam_authenticate(pamh, PAM_SILENT);
if (pam_err != PAM_SUCCESS)
{
printf("Pam Error (%d)\n", pam_err);
warn("pam_authenticate");
assert(false);
}
printf("AUTHENTICATED ;-)");
assert(false);
if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
if (pam_err != PAM_SUCCESS)
{
assert(false);
}
/* establish the requested credentials */
if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
{
assert(false);
}
/* authentication succeeded; open a session */
if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
{
assert(false);
}
/* get mapped user name; PAM may have changed it */
pam_err = pam_get_item(pamh, PAM_USER, (const void **)&user);
if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user)) == NULL)
{
assert(false);
}
/* export PAM environment */
if ((pam_envlist = pam_getenvlist(pamh)) != NULL) {
for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
putenv(*pam_env);
free(*pam_env);
}
free(pam_envlist);
}
std::vector<std::string> arguments;
arguments.resize(argc + 2);
char * args_ptr [arguments.size()];
arguments[0] = pwd->pw_shell;
args_ptr[argc +1] = NULL;
args_ptr[0] = (char *)arguments[0].c_str();
for (int i = 0; i < argc; i++)
{
arguments[i + 1] = argv[i];
args_ptr[i+1] = (char *)arguments[i+1].c_str();
}
/* set uid and groups */
if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
warn("initgroups()");
_exit(1);
}
if (setgid(pwd->pw_gid) == -1) {
warn("setgid()");
_exit(1);
}
if (setuid(pwd->pw_uid) == -1) {
warn("setuid()");
_exit(1);
}
execve(args_ptr[0], args_ptr, environ);
warn("execve()");
_exit(1);
pamerr:
fprintf(stderr, "Sorry\n");
err:
pam_end(pamh, pam_err);
exit(1);
}
Я ожидаю раскошелиться на повышенного ребенка и запустить мою новую программу, не спрашивая пароль.