2000-12-26 19:41:33 +00:00
|
|
|
/* -*-Mode: c-*- */
|
|
|
|
/* Copyright (C) 1997 Red Hat Software, Inc.
|
2001-01-24 20:26:38 +00:00
|
|
|
* Copyright 2001 Ximian Inc.
|
2000-12-26 19:41:33 +00:00
|
|
|
*
|
|
|
|
* This is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* UserTool suid helper program
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
#define USE_MCHECK 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define DEBUG_USERHELPER 1
|
|
|
|
#define ROOT_MANAGER 1
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#ifdef USE_MCHECK
|
|
|
|
#include <mcheck.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <locale.h>
|
|
|
|
#include <libintl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <pwd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/resource.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <security/pam_appl.h>
|
|
|
|
#include <security/pam_misc.h>
|
|
|
|
|
|
|
|
#include "root-manager.h"
|
|
|
|
#include "shvar.h"
|
|
|
|
|
2001-01-24 20:26:38 +00:00
|
|
|
#define _(s) gettext(s)
|
|
|
|
|
2000-12-26 19:41:33 +00:00
|
|
|
/* Total GECOS field length... is this enough ? */
|
|
|
|
#define GECOS_LENGTH 80
|
|
|
|
|
|
|
|
#ifndef TRUE
|
|
|
|
#define TRUE 1
|
|
|
|
#endif
|
|
|
|
#ifndef FALSE
|
|
|
|
#define FALSE 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/* ------ some static data objects ------- */
|
|
|
|
|
|
|
|
static char *full_name = NULL; /* full user name */
|
|
|
|
static char *office = NULL; /* office */
|
|
|
|
static char *office_ph = NULL; /* office phone */
|
|
|
|
static char *home_ph = NULL; /* home phone */
|
|
|
|
static char *user_name = NULL; /* the account name */
|
|
|
|
static char *shell_path = NULL; /* shell path */
|
|
|
|
|
|
|
|
static char *the_username = NULL; /* used to mangle the conversation function */
|
|
|
|
|
|
|
|
/* manipulate the environment directly */
|
|
|
|
extern char **environ;
|
|
|
|
|
|
|
|
/* command line flags */
|
|
|
|
static int f_flg = 0; /* -f flag = change full name */
|
|
|
|
static int o_flg = 0; /* -o flag = change office name */
|
|
|
|
static int p_flg = 0; /* -p flag = change office phone */
|
|
|
|
static int h_flg = 0; /* -h flag = change home phone number */
|
|
|
|
static int c_flg = 0; /* -c flag = change password */
|
|
|
|
static int s_flg = 0; /* -s flag = change shell */
|
|
|
|
static int t_flg = 0; /* -t flag = direct text-mode -- exec'ed */
|
|
|
|
static int w_flg = 0; /* -w flag = act as a wrapper for next args */
|
|
|
|
static int d_flg = 0; /* -d flag = three descriptor numbers for us */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A handy fail exit function we can call from many places
|
|
|
|
*/
|
|
|
|
static int fail_error(int retval)
|
|
|
|
{
|
|
|
|
/* this is a temporary kludge.. will be fixed shortly. */
|
|
|
|
if(retval == ERR_SHELL_INVALID)
|
|
|
|
exit(ERR_SHELL_INVALID);
|
|
|
|
|
|
|
|
if (retval != PAM_SUCCESS) {
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
g_print(_("Got error %d.\n"), retval);
|
|
|
|
#endif
|
|
|
|
switch(retval) {
|
|
|
|
case PAM_AUTH_ERR:
|
|
|
|
case PAM_PERM_DENIED:
|
|
|
|
exit (ERR_PASSWD_INVALID);
|
|
|
|
case PAM_AUTHTOK_LOCK_BUSY:
|
|
|
|
exit (ERR_LOCKS);
|
|
|
|
case PAM_CRED_INSUFFICIENT:
|
|
|
|
case PAM_AUTHINFO_UNAVAIL:
|
|
|
|
exit (ERR_NO_RIGHTS);
|
|
|
|
case PAM_ABORT:
|
|
|
|
default:
|
|
|
|
exit(ERR_UNK_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a string from stdin, returns a malloced copy of it
|
|
|
|
*/
|
|
|
|
static char *read_string(void)
|
|
|
|
{
|
|
|
|
char *buffer = NULL;
|
|
|
|
char *check = NULL;
|
|
|
|
int slen = 0;
|
|
|
|
|
|
|
|
buffer = g_malloc(BUFSIZ);
|
|
|
|
if (buffer == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
check = fgets(buffer, BUFSIZ, stdin);
|
|
|
|
if (!check)
|
|
|
|
return NULL;
|
|
|
|
slen = strlen(buffer);
|
|
|
|
if((slen > 0) && ((buffer[slen - 1] == '\n') || isspace(buffer[slen - 1]))){
|
|
|
|
buffer[slen-1] = '\0';
|
|
|
|
}
|
|
|
|
if(buffer[0] == UH_TEXT) {
|
|
|
|
memmove(buffer, buffer + 1, BUFSIZ - 1);
|
|
|
|
}
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Application data with some hints. */
|
|
|
|
static struct app_data_t {
|
|
|
|
int fallback;
|
|
|
|
char *user;
|
|
|
|
char *service;
|
|
|
|
} app_data = {0, NULL, NULL};
|
|
|
|
static gboolean fallback_flag = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Conversation function for the boring change password stuff
|
|
|
|
*/
|
|
|
|
static int conv_func(int num_msg, const struct pam_message **msg,
|
|
|
|
struct pam_response **resp, void *appdata_ptr)
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
int responses = 0;
|
|
|
|
struct pam_response *reply = NULL;
|
|
|
|
char *noecho_message;
|
|
|
|
|
|
|
|
reply = (struct pam_response *)
|
|
|
|
calloc(num_msg, sizeof(struct pam_response));
|
|
|
|
if (reply == NULL)
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
|
|
|
|
if(appdata_ptr != NULL) {
|
|
|
|
struct app_data_t *app_data = (struct app_data_t*) appdata_ptr;
|
|
|
|
|
|
|
|
g_print("%d %d\n", UH_FALLBACK, app_data->fallback);
|
|
|
|
if(app_data->user == NULL) {
|
|
|
|
g_print("%d %s\n", UH_USER, "root");
|
|
|
|
} else {
|
|
|
|
g_print("%d %s\n", UH_USER, app_data->user);
|
|
|
|
}
|
|
|
|
if(app_data->service != NULL) {
|
|
|
|
g_print("%d %s\n", UH_SERVICE_NAME, app_data->service);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We do first a pass on all items and output them;
|
|
|
|
* then we do a second pass and read what we have to read
|
|
|
|
* from stdin
|
|
|
|
*/
|
|
|
|
for (count = responses = 0; count < num_msg; count++) {
|
|
|
|
switch (msg[count]->msg_style) {
|
|
|
|
case PAM_PROMPT_ECHO_ON:
|
|
|
|
g_print("%d %s\n", UH_ECHO_ON_PROMPT, msg[count]->msg);
|
|
|
|
responses++;
|
|
|
|
break;
|
|
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
|
|
if (the_username && !strncasecmp(msg[count]->msg, "password", 8)) {
|
|
|
|
noecho_message = g_strdup_printf(_("Password for %s"),
|
|
|
|
the_username);
|
|
|
|
} else {
|
|
|
|
noecho_message = g_strdup(msg[count]->msg);
|
|
|
|
}
|
|
|
|
g_print("%d %s\n", UH_ECHO_OFF_PROMPT, noecho_message);
|
|
|
|
g_free(noecho_message);
|
|
|
|
responses++;
|
|
|
|
break;
|
|
|
|
case PAM_TEXT_INFO:
|
|
|
|
g_print("%d %s\n", UH_INFO_MSG, msg[count]->msg);
|
|
|
|
break;
|
|
|
|
case PAM_ERROR_MSG:
|
|
|
|
g_print("%d %s\n", UH_ERROR_MSG, msg[count]->msg);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_print("0 %s\n", msg[count]->msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* tell the other side how many messages we expect responses for */
|
|
|
|
g_print("%d %d\n", UH_EXPECT_RESP, responses);
|
|
|
|
fflush(NULL);
|
|
|
|
|
|
|
|
/* now the second pass */
|
|
|
|
for (count = 0; count < num_msg; count++) {
|
|
|
|
switch (msg[count]->msg_style) {
|
|
|
|
case PAM_TEXT_INFO:
|
|
|
|
/* ignore it... */
|
|
|
|
break;
|
|
|
|
case PAM_ERROR_MSG:
|
|
|
|
/* also ignore it... */
|
|
|
|
break;
|
|
|
|
case PAM_PROMPT_ECHO_ON:
|
|
|
|
/* fall through */
|
|
|
|
case PAM_PROMPT_ECHO_OFF:
|
|
|
|
reply[count].resp_retcode = PAM_SUCCESS;
|
|
|
|
reply[count].resp = read_string();
|
|
|
|
if((reply[count].resp != NULL) &&
|
|
|
|
(reply[count].resp[0] == UH_ABORT)) {
|
|
|
|
fallback_flag = TRUE;
|
|
|
|
free (reply);
|
|
|
|
return PAM_MAXTRIES; /* Shrug. */
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Must be an error of some sort... */
|
|
|
|
free (reply);
|
|
|
|
return PAM_CONV_ERR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (reply)
|
|
|
|
*resp = reply;
|
|
|
|
return PAM_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the structure pointing at the conversation function for
|
|
|
|
* auth and changing the password
|
|
|
|
*/
|
|
|
|
static struct pam_conv pipe_conv = {
|
|
|
|
conv_func,
|
|
|
|
&app_data,
|
|
|
|
};
|
|
|
|
static struct pam_conv text_conv = {
|
|
|
|
misc_conv,
|
|
|
|
&app_data,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A function to process already existing gecos information
|
|
|
|
*/
|
|
|
|
static void process_gecos(char *gecos)
|
|
|
|
{
|
|
|
|
char *idx;
|
|
|
|
|
|
|
|
if (gecos == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!full_name)
|
|
|
|
full_name = gecos;
|
|
|
|
idx = strchr(gecos, ',');
|
|
|
|
if (idx != NULL) {
|
|
|
|
*idx = '\0';
|
|
|
|
gecos = idx+1;
|
|
|
|
}
|
|
|
|
if ((idx == NULL) || (*gecos == '\0')) {
|
|
|
|
/* no more fields */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!office)
|
|
|
|
office = gecos;
|
|
|
|
idx = strchr(gecos, ',');
|
|
|
|
if (idx != NULL) {
|
|
|
|
*idx = '\0';
|
|
|
|
gecos = idx+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!office_ph)
|
|
|
|
office_ph = gecos;
|
|
|
|
idx = strchr(gecos, ',');
|
|
|
|
if (idx != NULL) {
|
|
|
|
*idx = '\0';
|
|
|
|
gecos = idx+1;
|
|
|
|
}
|
|
|
|
if ((idx == NULL) || (*gecos == '\0')) {
|
|
|
|
/* no more fields */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!home_ph)
|
|
|
|
home_ph = gecos;
|
|
|
|
idx = strchr(gecos, ',');
|
|
|
|
if (idx != NULL) {
|
|
|
|
*idx = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* invalid_field - insure that a field contains all legal characters
|
|
|
|
*
|
|
|
|
* The supplied field is scanned for non-printing and other illegal
|
|
|
|
* characters. If any illegal characters are found, invalid_field
|
|
|
|
* returns -1. Zero is returned for success.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int invalid_field(const char *field, const char *illegal)
|
|
|
|
{
|
|
|
|
const char *cp;
|
|
|
|
|
|
|
|
for (cp = field; *cp && isprint (*cp) && ! strchr (illegal, *cp); cp++)
|
|
|
|
;
|
|
|
|
if (*cp)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A simple function to compute the gecos field size
|
|
|
|
*/
|
|
|
|
static int gecos_size(void)
|
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
if (full_name != NULL)
|
|
|
|
len += strlen(full_name);
|
|
|
|
if (office != NULL)
|
|
|
|
len += strlen(office);
|
|
|
|
if (office_ph != NULL)
|
|
|
|
len += strlen(office_ph);
|
|
|
|
if (home_ph != NULL)
|
|
|
|
len += strlen(home_ph);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Snagged straight from the util-linux source... May want to clean up
|
|
|
|
* a bit and possibly merge with the code in userinfo that parses to
|
|
|
|
* get a list. -Otto
|
|
|
|
*
|
|
|
|
* get_shell_list () -- if the given shell appears in the list of valid shells,
|
|
|
|
* return true. if not, return false.
|
|
|
|
* if the given shell is NULL, the list of shells is outputted to stdout.
|
|
|
|
*/
|
|
|
|
static int get_shell_list(char* shell_name)
|
|
|
|
{
|
|
|
|
gboolean found;
|
|
|
|
char *shell;
|
|
|
|
|
|
|
|
found = FALSE;
|
|
|
|
setusershell();
|
|
|
|
for(shell = getusershell(); shell != NULL; shell = getusershell()) {
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf(stderr, "got shell \"%s\"\n", shell);
|
|
|
|
#endif
|
|
|
|
if (shell_name) {
|
|
|
|
if (! strcmp (shell_name, shell)) {
|
|
|
|
found = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else g_print("%s\n", shell);
|
|
|
|
}
|
|
|
|
endusershell();
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_MCHECK
|
|
|
|
void
|
|
|
|
mcheck_out(enum mcheck_status reason) {
|
|
|
|
char *explanation;
|
|
|
|
|
|
|
|
switch (reason) {
|
|
|
|
case MCHECK_DISABLED:
|
|
|
|
explanation = _("Consistency checking is not turned on."); break;
|
|
|
|
case MCHECK_OK:
|
|
|
|
explanation = _("Block is fine."); break;
|
|
|
|
case MCHECK_FREE:
|
|
|
|
explanation = _("Block freed twice."); break;
|
|
|
|
case MCHECK_HEAD:
|
|
|
|
explanation = _("Memory before the block was clobbered."); break;
|
|
|
|
case MCHECK_TAIL:
|
|
|
|
explanation = _("Memory after the block was clobbered."); break;
|
|
|
|
}
|
|
|
|
g_print("%d %s\n", UH_ERROR_MSG, explanation);
|
|
|
|
g_print("%d 1\n", UH_EXPECT_RESP);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* ------- the application itself -------- */
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int arg;
|
|
|
|
int retval;
|
|
|
|
char *progname = NULL;
|
|
|
|
pam_handle_t *pamh = NULL;
|
|
|
|
struct passwd *pw;
|
|
|
|
struct pam_conv *conv;
|
|
|
|
int stdin_fileno = -1, stdout_fileno = -1, stderr_fileno = -1;
|
|
|
|
int fd;
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
#ifdef USE_MCHECK
|
|
|
|
mtrace();
|
|
|
|
mcheck(mcheck_out);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bindtextdomain (PACKAGE, GNOMELOCALEDIR);
|
2001-12-09 12:23:40 +00:00
|
|
|
bind_textdomain_codeset (PACKAGE, "UTF-8");
|
2000-12-26 19:41:33 +00:00
|
|
|
textdomain (PACKAGE);
|
|
|
|
|
|
|
|
if (geteuid() != 0) {
|
|
|
|
fprintf(stderr, _("userhelper must be setuid root\n"));
|
|
|
|
exit(ERR_NO_RIGHTS);
|
|
|
|
}
|
|
|
|
|
2001-01-24 20:26:38 +00:00
|
|
|
if (argc < 2) {
|
2001-01-25 17:40:43 +00:00
|
|
|
fprintf(stderr, _("Usage: root-helper fd\n"));
|
2001-01-24 20:26:38 +00:00
|
|
|
exit(ERR_INVALID_CALL);
|
|
|
|
}
|
2000-12-26 19:41:33 +00:00
|
|
|
|
|
|
|
fd = atoi (argv[1]);
|
2001-01-24 20:26:38 +00:00
|
|
|
if (fd <= STDERR_FILENO) {
|
2001-01-25 17:40:43 +00:00
|
|
|
fprintf (stderr, _("Usage: root-helper fd\n"));
|
2001-01-24 20:26:38 +00:00
|
|
|
exit(ERR_INVALID_CALL);
|
|
|
|
}
|
2000-12-26 19:41:33 +00:00
|
|
|
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf (stderr, "fd is %d\n", fd);
|
|
|
|
#endif
|
|
|
|
fp = fdopen (fd, "r");
|
|
|
|
|
|
|
|
conv = &pipe_conv;
|
|
|
|
|
|
|
|
/* now try to identify the username we are doing all this work for */
|
|
|
|
user_name = getlogin();
|
|
|
|
if (user_name == NULL) {
|
|
|
|
struct passwd *tmp;
|
|
|
|
|
|
|
|
tmp = getpwuid(getuid());
|
|
|
|
if ((tmp != NULL) && (tmp->pw_name != NULL)) {
|
|
|
|
user_name = g_strdup(tmp->pw_name);
|
|
|
|
} else {
|
|
|
|
/* weirdo, bail out */
|
|
|
|
exit (ERR_UNK_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf(stderr, "user is %s\n", user_name);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{
|
|
|
|
/* pick the first existing of /usr/sbin/<progname> and /sbin/<progname>
|
|
|
|
* authenticate <progname>
|
|
|
|
* argv[optind-1] = <progname> (boondoggle unless -- used)
|
|
|
|
* (we know there is at least one open slot in argv for this)
|
|
|
|
* execv(constructed_path, argv+optind);
|
|
|
|
*/
|
|
|
|
char *constructed_path;
|
|
|
|
char *apps_filename;
|
|
|
|
char *user, *apps_user;
|
|
|
|
char *retry;
|
|
|
|
char *env_home, *env_term, *env_display, *env_shell;
|
|
|
|
char *env_user, *env_logname, *env_lang, *env_lcall, *env_lcmsgs;
|
|
|
|
char *env_xauthority;
|
|
|
|
int session, fallback, try;
|
|
|
|
size_t aft;
|
|
|
|
struct stat sbuf;
|
|
|
|
shvarFile *s;
|
|
|
|
|
|
|
|
env_home = getenv("HOME");
|
|
|
|
env_term = getenv("TERM");
|
|
|
|
env_display = getenv("DISPLAY");
|
|
|
|
env_shell = getenv("SHELL");
|
|
|
|
env_user = getenv("USER");
|
|
|
|
env_logname = getenv("LOGNAME");
|
|
|
|
env_lang = getenv("LANG");
|
|
|
|
env_lcall = getenv("LC_ALL");
|
|
|
|
env_lcmsgs = getenv("LC_MESSAGES");
|
|
|
|
env_xauthority = getenv("XAUTHORITY");
|
|
|
|
|
|
|
|
if (env_home && (strstr(env_home, "..") || strchr(env_home, '%')))
|
|
|
|
env_home=NULL;
|
|
|
|
if (env_shell && (strstr(env_shell, "..") || strchr(env_shell, '%')))
|
|
|
|
env_shell=NULL;
|
|
|
|
if (env_term && (strstr(env_term, "..") || strchr(env_term, '%')))
|
|
|
|
env_term="dumb";
|
|
|
|
if (env_lang && (strchr(env_lang, '/') || strchr(env_lang, '%')))
|
|
|
|
env_lang=NULL;
|
|
|
|
if (env_lcall && (strchr(env_lcall, '/') || strchr(env_lcall, '%')))
|
|
|
|
env_lcall=NULL;
|
|
|
|
if (env_lcmsgs && (strchr(env_lcmsgs, '/') || strchr(env_lcmsgs, '%')))
|
|
|
|
env_lcmsgs=NULL;
|
|
|
|
|
|
|
|
environ = (char **) calloc (1, 2 * sizeof (char *));
|
|
|
|
/* note that XAUTHORITY not copied -- do not let attackers get at
|
|
|
|
* others' X authority records
|
|
|
|
*/
|
|
|
|
if (env_home) setenv("HOME", env_home, 1);
|
|
|
|
if (env_term) setenv("TERM", env_term, 1);
|
|
|
|
if (env_display) setenv("DISPLAY", env_display, 1);
|
|
|
|
if (env_shell) setenv("SHELL", env_shell, 1);
|
|
|
|
if (env_user) setenv("USER", env_user, 1);
|
|
|
|
if (env_logname) setenv("LOGNAME", env_logname, 1);
|
|
|
|
/* we want _, but only if it is safe */
|
|
|
|
if (env_lang) setenv("LANG", env_lang, 1);
|
|
|
|
if (env_lcall) setenv("LC_ALL", env_lcall, 1);
|
|
|
|
if (env_lcmsgs) setenv("LC_MESSAGES", env_lcmsgs, 1);
|
|
|
|
|
|
|
|
setenv("PATH",
|
|
|
|
"/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/root/bin", 1);
|
|
|
|
|
|
|
|
progname = "root-manager";
|
|
|
|
user = "root";
|
|
|
|
constructed_path = "cat";
|
|
|
|
|
|
|
|
retval = pam_start(progname, user, conv, &pamh);
|
|
|
|
if (retval != PAM_SUCCESS)
|
|
|
|
fail_error(retval);
|
|
|
|
|
|
|
|
session = TRUE;
|
|
|
|
app_data.fallback = fallback = FALSE;
|
|
|
|
app_data.user = user;
|
|
|
|
app_data.service = progname;
|
|
|
|
|
|
|
|
do {
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf(stderr, _("PAM returned = %d\n"), retval);
|
|
|
|
fprintf(stderr, _("about to authenticate \"%s\"\n"), user);
|
|
|
|
#endif
|
|
|
|
retval = pam_authenticate(pamh, 0);
|
|
|
|
} while (--try && retval != PAM_SUCCESS && !fallback_flag);
|
|
|
|
if (retval != PAM_SUCCESS) {
|
|
|
|
pam_end(pamh, retval);
|
|
|
|
if (fallback) {
|
|
|
|
g_assert_not_reached ();
|
|
|
|
setuid(getuid());
|
|
|
|
if(geteuid() != getuid()) {
|
|
|
|
exit (ERR_EXEC_FAILED);
|
|
|
|
}
|
|
|
|
argv[optind-1] = progname;
|
|
|
|
if(d_flg) {
|
|
|
|
dup2(stdin_fileno, STDIN_FILENO);
|
|
|
|
dup2(stdout_fileno, STDOUT_FILENO);
|
|
|
|
dup2(stderr_fileno, STDERR_FILENO);
|
|
|
|
}
|
|
|
|
execv(constructed_path, argv+optind-1);
|
|
|
|
exit (ERR_EXEC_FAILED);
|
|
|
|
} else {
|
|
|
|
fail_error(retval);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
retval = pam_acct_mgmt(pamh, 0);
|
|
|
|
if (retval != PAM_SUCCESS) {
|
|
|
|
pam_end(pamh, retval);
|
|
|
|
fail_error(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* reset the XAUTHORITY so that X stuff will work now */
|
|
|
|
if (env_xauthority) setenv("XAUTHORITY", env_xauthority, 1);
|
|
|
|
|
|
|
|
if (session) {
|
|
|
|
int child, status;
|
|
|
|
|
|
|
|
retval = pam_open_session(pamh, 0);
|
|
|
|
if (retval != PAM_SUCCESS) {
|
|
|
|
pam_end(pamh, retval);
|
|
|
|
fail_error(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((child = fork()) == 0) {
|
|
|
|
struct passwd *pw;
|
|
|
|
setuid(0);
|
|
|
|
pw = getpwuid(getuid());
|
|
|
|
if (pw) setenv("HOME", pw->pw_dir, 1);
|
|
|
|
|
|
|
|
#define BUFSIZE 1024
|
|
|
|
{
|
|
|
|
char buf[BUFSIZE];
|
2001-01-24 20:26:38 +00:00
|
|
|
fprintf (stdout, "success\n");
|
2000-12-26 19:41:33 +00:00
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf (stderr, "looping for input\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (fgets (buf, BUFSIZE, fp)) {
|
|
|
|
if (!fork ()) {
|
|
|
|
char **args;
|
|
|
|
buf[strlen (buf) - 1] = '\0';
|
|
|
|
args = g_strsplit (buf, " ", -1);
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf (stderr, "running: %s\n", args[0]);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
setenv("PATH",
|
|
|
|
"/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/root/bin", 1);
|
|
|
|
|
|
|
|
execv (args[0], args);
|
2001-01-24 20:26:38 +00:00
|
|
|
g_error ("%s", g_strerror (errno));
|
2000-12-26 19:41:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
argv[optind-1] = progname;
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
g_print(_("about to exec \"%s\"\n"), constructed_path);
|
|
|
|
#endif
|
|
|
|
if(d_flg) {
|
|
|
|
dup2(stdin_fileno, STDIN_FILENO);
|
|
|
|
dup2(stdout_fileno, STDOUT_FILENO);
|
|
|
|
dup2(stderr_fileno, STDERR_FILENO);
|
|
|
|
}
|
|
|
|
execv(constructed_path, argv+optind-1);
|
|
|
|
exit (ERR_EXEC_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
close(STDIN_FILENO);
|
|
|
|
close(STDOUT_FILENO);
|
|
|
|
close(STDERR_FILENO);
|
|
|
|
|
|
|
|
wait4 (child, &status, 0, NULL);
|
|
|
|
|
|
|
|
retval = pam_close_session(pamh, 0);
|
|
|
|
if (retval != PAM_SUCCESS) {
|
|
|
|
pam_end(pamh, retval);
|
|
|
|
fail_error(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)) {
|
|
|
|
pam_end(pamh, PAM_SUCCESS);
|
|
|
|
retval = 1;
|
|
|
|
} else {
|
|
|
|
pam_end(pamh, PAM_SUCCESS);
|
|
|
|
retval = ERR_EXEC_FAILED;
|
|
|
|
}
|
|
|
|
exit (retval);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* this is not a session, so do not do session management */
|
|
|
|
|
|
|
|
pam_end(pamh, PAM_SUCCESS);
|
|
|
|
|
|
|
|
|
|
|
|
#define BUFSIZE 1024
|
|
|
|
{
|
|
|
|
char buf[BUFSIZE];
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf (stderr, "looping for input\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (fgets (buf, BUFSIZE, fp)) {
|
|
|
|
if (!fork ()) {
|
|
|
|
char **args;
|
|
|
|
buf[strlen (buf) - 1] = '\0';
|
|
|
|
args = g_strsplit (buf, " ", -1);
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
fprintf (stderr, "running: %s\n", args[0]);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
setenv("PATH",
|
|
|
|
"/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/root/bin", 1);
|
|
|
|
|
|
|
|
execv (args[0], args);
|
2001-01-24 20:26:38 +00:00
|
|
|
g_error ("%s", g_strerror (errno));
|
2000-12-26 19:41:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* time for an exec */
|
|
|
|
setuid(0);
|
|
|
|
argv[optind-1] = progname;
|
|
|
|
#ifdef DEBUG_USERHELPER
|
|
|
|
g_print(_("about to exec \"%s\"\n"), constructed_path);
|
|
|
|
#endif
|
|
|
|
if(d_flg) {
|
|
|
|
dup2(stdin_fileno, STDIN_FILENO);
|
|
|
|
dup2(stdout_fileno, STDOUT_FILENO);
|
|
|
|
dup2(stderr_fileno, STDERR_FILENO);
|
|
|
|
}
|
|
|
|
execv(constructed_path, argv+optind-1);
|
|
|
|
|
|
|
|
exit (ERR_EXEC_FAILED);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* all done */
|
|
|
|
if (pamh != NULL)
|
|
|
|
retval = pam_end(pamh, PAM_SUCCESS);
|
|
|
|
if (retval != PAM_SUCCESS)
|
|
|
|
fail_error(retval);
|
|
|
|
exit (0);
|
|
|
|
}
|