|
|
@ -25,6 +25,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include <sys/types.h> |
|
|
|
#include <sys/types.h> |
|
|
|
#include <sys/wait.h> |
|
|
|
#include <sys/wait.h> |
|
|
|
|
|
|
|
#include <sys/stat.h> |
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <stdlib.h> |
|
|
@ -36,6 +37,8 @@ |
|
|
|
#include <grp.h> |
|
|
|
#include <grp.h> |
|
|
|
#include <termios.h> |
|
|
|
#include <termios.h> |
|
|
|
#include <stdarg.h> |
|
|
|
#include <stdarg.h> |
|
|
|
|
|
|
|
#include <ctype.h> |
|
|
|
|
|
|
|
#include <signal.h> |
|
|
|
|
|
|
|
|
|
|
|
#if !defined(_XOPEN_CRYPT) || _XOPEN_CRYPT == -1 |
|
|
|
#if !defined(_XOPEN_CRYPT) || _XOPEN_CRYPT == -1 |
|
|
|
#include <crypt.h> |
|
|
|
#include <crypt.h> |
|
|
@ -46,30 +49,51 @@ |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#define MAX_HASH 1024 |
|
|
|
#define MAX_HASH 1024 |
|
|
|
|
|
|
|
#define CONF_LINE_MAX 1024 |
|
|
|
|
|
|
|
#define GROUPS_MAX 256 |
|
|
|
|
|
|
|
#define FLAG_PERSIST 0x1 |
|
|
|
|
|
|
|
#define FLAG_NOPASS 0x2 |
|
|
|
|
|
|
|
#define FLAG_NOLOG 0x4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct env_elem { |
|
|
|
|
|
|
|
char *name; |
|
|
|
|
|
|
|
char *value; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct config { |
|
|
|
|
|
|
|
int type; |
|
|
|
|
|
|
|
int flags; |
|
|
|
|
|
|
|
char *who; |
|
|
|
|
|
|
|
char *as; |
|
|
|
|
|
|
|
struct env_elem *env; |
|
|
|
|
|
|
|
int env_n; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void *emalloc(size_t); |
|
|
|
|
|
|
|
static char *estrdup(const char *); |
|
|
|
|
|
|
|
void *erealloc(void *, size_t); |
|
|
|
static void usage(void); |
|
|
|
static void usage(void); |
|
|
|
static void die(const char *, ...); |
|
|
|
static void die(const char *, ...); |
|
|
|
static int perm_set(struct passwd *, struct group *); |
|
|
|
static int perm_set(struct passwd *, struct group *); |
|
|
|
static int authenticate(uid_t, char); |
|
|
|
static int authenticate(uid_t, char); |
|
|
|
static struct passwd* user_to_passwd(const char *); |
|
|
|
static struct passwd* user_to_passwd(const char *); |
|
|
|
static struct group* group_to_grp(const char *); |
|
|
|
static struct group* group_to_grp(const char *); |
|
|
|
//static int execvpe(const char *, char *const *, char *const *);
|
|
|
|
static int get_config(struct config **, int *); |
|
|
|
|
|
|
|
|
|
|
|
extern char **environ; |
|
|
|
extern char **environ; |
|
|
|
|
|
|
|
char *config_file = "/etc/us.conf"; |
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char *argv[]) |
|
|
|
int main(int argc, char *argv[]) |
|
|
|
{ |
|
|
|
{ |
|
|
|
// TODO: modify signal handlers to ignore sigchld to not make zombies
|
|
|
|
|
|
|
|
char *t_usr = "root", *t_grp = NULL; |
|
|
|
char *t_usr = "root", *t_grp = NULL; |
|
|
|
struct passwd *t_pw; |
|
|
|
struct passwd *t_pw; |
|
|
|
struct group *t_gr; |
|
|
|
struct group *t_gr; |
|
|
|
int opt, err; |
|
|
|
int opt, err; |
|
|
|
int shellflag = 0, envflag = 0; |
|
|
|
int shellflag = 0, envflag = 0, askpass = 0; |
|
|
|
while ((opt = getopt(argc, argv, "A:u:g:C:se")) != -1) { |
|
|
|
while ((opt = getopt(argc, argv, "Au:g:C:se")) != -1) { |
|
|
|
switch (opt) { |
|
|
|
switch (opt) { |
|
|
|
case 'A': |
|
|
|
case 'A': |
|
|
|
printf("-A is not yet implemented\n"); |
|
|
|
askpass = 1; |
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
|
|
|
break; |
|
|
|
break; |
|
|
|
case 'u': |
|
|
|
case 'u': |
|
|
|
t_usr = optarg; |
|
|
|
t_usr = optarg; |
|
|
@ -78,8 +102,7 @@ int main(int argc, char *argv[]) |
|
|
|
t_grp = optarg; |
|
|
|
t_grp = optarg; |
|
|
|
break; |
|
|
|
break; |
|
|
|
case 'C': |
|
|
|
case 'C': |
|
|
|
printf("-C is not yet implemented\n"); |
|
|
|
config_file = optarg; |
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
|
|
|
break; |
|
|
|
break; |
|
|
|
case 's': |
|
|
|
case 's': |
|
|
|
shellflag = 1; |
|
|
|
shellflag = 1; |
|
|
@ -102,20 +125,99 @@ int main(int argc, char *argv[]) |
|
|
|
fprintf(stderr, "getpwid: %s\n", strerror(errno)); |
|
|
|
fprintf(stderr, "getpwid: %s\n", strerror(errno)); |
|
|
|
return errno; |
|
|
|
return errno; |
|
|
|
} |
|
|
|
} |
|
|
|
char *my_name = strdup(my_pw->pw_name); |
|
|
|
char *my_name = my_pw->pw_name; |
|
|
|
if (!my_name) |
|
|
|
gid_t my_groups[GROUPS_MAX]; |
|
|
|
die("strdup:"); |
|
|
|
int n_groups = 0; |
|
|
|
|
|
|
|
if ((n_groups = getgroups(GROUPS_MAX, my_groups)) == -1) |
|
|
|
/* Authenticate, we will be root from now on */ |
|
|
|
die("getgroups:"); |
|
|
|
if (authenticate(my_pw->pw_uid, 0)) |
|
|
|
|
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Get target user and group info */ |
|
|
|
/* Get target user and group info */ |
|
|
|
t_pw = user_to_passwd(t_usr); |
|
|
|
t_pw = user_to_passwd(t_usr); |
|
|
|
if (!t_pw) |
|
|
|
if (!t_pw) |
|
|
|
die("usr_to_passwd:"); |
|
|
|
die("user_to_passwd:"); |
|
|
|
t_gr = group_to_grp(t_grp); |
|
|
|
t_gr = group_to_grp(t_grp); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Don't have to wait for children */ |
|
|
|
|
|
|
|
struct sigaction sa = {0}; |
|
|
|
|
|
|
|
sa.sa_handler = SIG_DFL; |
|
|
|
|
|
|
|
sa.sa_flags = SA_NOCLDWAIT; |
|
|
|
|
|
|
|
if (sigaction(SIGCHLD, &sa, NULL) == -1) |
|
|
|
|
|
|
|
die("sigaction:"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* From now on most actions require root */ |
|
|
|
|
|
|
|
if (setuid(0) == -1) |
|
|
|
|
|
|
|
die("setuid:"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* get info from the config file and check if the action we want to
|
|
|
|
|
|
|
|
* do is permitted */ |
|
|
|
|
|
|
|
struct env_elem *env_extra = NULL; |
|
|
|
|
|
|
|
struct config *conf = NULL; |
|
|
|
|
|
|
|
int conf_num, conf_flags = 0, env_extra_n = 0; |
|
|
|
|
|
|
|
if (get_config(&conf, &conf_num) == -1) |
|
|
|
|
|
|
|
die("get_config: invalid arguments"); |
|
|
|
|
|
|
|
int here = 0; |
|
|
|
|
|
|
|
for (int i = 0; i < conf_num; i++) { |
|
|
|
|
|
|
|
struct passwd *who_pw, *as_pw; |
|
|
|
|
|
|
|
struct group *who_gr, *as_gr; |
|
|
|
|
|
|
|
int who_usr = conf[i].who[0] == ':' ? 0 : 1; |
|
|
|
|
|
|
|
int as_usr = conf[i].as[0] == ':' ? 0 : 1; |
|
|
|
|
|
|
|
if (who_usr) { |
|
|
|
|
|
|
|
who_pw = user_to_passwd(conf[i].who); |
|
|
|
|
|
|
|
if (my_pw->pw_uid != who_pw->pw_uid) { |
|
|
|
|
|
|
|
free(who_pw); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
who_gr = group_to_grp(conf[i].who); |
|
|
|
|
|
|
|
gid_t w_gid = who_gr->gr_gid; |
|
|
|
|
|
|
|
int x = 0; |
|
|
|
|
|
|
|
for (; x < n_groups && w_gid != my_groups[x]; x++); |
|
|
|
|
|
|
|
if (w_gid != my_groups[x]) { |
|
|
|
|
|
|
|
free(who_gr); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (as_usr) { |
|
|
|
|
|
|
|
as_pw = user_to_passwd(conf[i].as); |
|
|
|
|
|
|
|
if (!as_pw) |
|
|
|
|
|
|
|
die("%s not a valid user", conf[i].as); |
|
|
|
|
|
|
|
if (t_pw->pw_uid != as_pw->pw_uid) { |
|
|
|
|
|
|
|
free(as_pw); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (t_gr) { |
|
|
|
|
|
|
|
as_gr = group_to_grp(conf[i].as); |
|
|
|
|
|
|
|
if (t_gr->gr_gid != as_gr->gr_gid) { |
|
|
|
|
|
|
|
free(as_gr); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
here = 1; |
|
|
|
|
|
|
|
if (conf[i].type == 0) |
|
|
|
|
|
|
|
die("Permission denied"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
conf_flags |= conf[i].flags; |
|
|
|
|
|
|
|
if (conf[i].env_n) { |
|
|
|
|
|
|
|
env_extra = erealloc(env_extra, |
|
|
|
|
|
|
|
(env_extra_n+conf[i].env_n+1)*sizeof(struct env_elem)); |
|
|
|
|
|
|
|
for (int j = env_extra_n, x = 0; j < env_extra_n + conf[i].env_n; j++, x++) |
|
|
|
|
|
|
|
env_extra[j] = conf[i].env[x]; |
|
|
|
|
|
|
|
env_extra_n += conf[i].env_n; |
|
|
|
|
|
|
|
env_extra[env_extra_n] = (struct env_elem){NULL,NULL}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!here) |
|
|
|
|
|
|
|
die("no rule found for user %s", my_name); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Authenticate, we will be root from now on */ |
|
|
|
|
|
|
|
if (!(conf_flags & FLAG_NOPASS)) |
|
|
|
|
|
|
|
if (authenticate(my_pw->pw_uid, askpass)) |
|
|
|
|
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
|
|
|
|
|
|
|
/* Get target user's shell */ |
|
|
|
/* Get target user's shell */ |
|
|
|
if (!shellflag) |
|
|
|
if (!shellflag) |
|
|
|
shell = t_pw->pw_shell; |
|
|
|
shell = t_pw->pw_shell; |
|
|
@ -128,30 +230,16 @@ int main(int argc, char *argv[]) |
|
|
|
int c_argc = argc - optind; |
|
|
|
int c_argc = argc - optind; |
|
|
|
char **c_argv; |
|
|
|
char **c_argv; |
|
|
|
if (c_argc) { |
|
|
|
if (c_argc) { |
|
|
|
c_argv = malloc(sizeof(char *) * (c_argc + 1)); |
|
|
|
c_argv = emalloc(sizeof(char *) * (c_argc + 1)); |
|
|
|
if (!c_argv) |
|
|
|
for (int i = 0; optind < argc; optind++, i++) |
|
|
|
die("malloc:"); |
|
|
|
c_argv[i] = estrdup(argv[optind]); |
|
|
|
for (int i = 0; optind < argc; optind++, i++) { |
|
|
|
|
|
|
|
c_argv[i] = strdup(argv[optind]); |
|
|
|
|
|
|
|
if (!c_argv[i]) |
|
|
|
|
|
|
|
die("strdup:"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
c_argc = 1; |
|
|
|
c_argc = 1; |
|
|
|
c_argv = malloc(sizeof(char *) * (c_argc + 1)); |
|
|
|
c_argv = emalloc(sizeof(char *) * (c_argc + 1)); |
|
|
|
if (!c_argv) |
|
|
|
c_argv[0] = estrdup(shell); |
|
|
|
die("malloc:"); |
|
|
|
|
|
|
|
c_argv[0] = strdup(shell); |
|
|
|
|
|
|
|
if (!c_argv[0]) |
|
|
|
|
|
|
|
die("strdup:"); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
c_argv[c_argc] = NULL; |
|
|
|
c_argv[c_argc] = NULL; |
|
|
|
|
|
|
|
|
|
|
|
struct env_elem { |
|
|
|
|
|
|
|
char *name; |
|
|
|
|
|
|
|
char *value; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct env_elem env_keep[] = { |
|
|
|
struct env_elem env_keep[] = { |
|
|
|
{"PATH", NULL}, |
|
|
|
{"PATH", NULL}, |
|
|
|
{"TERM", NULL}, |
|
|
|
{"TERM", NULL}, |
|
|
@ -171,11 +259,8 @@ int main(int argc, char *argv[]) |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
if (envflag) { /* clear env */ |
|
|
|
if (envflag) { /* clear env */ |
|
|
|
for (int i = 0; env_keep[i].name; i++) { |
|
|
|
for (int i = 0; env_keep[i].name; i++) |
|
|
|
env_keep[i].value = strdup(getenv(env_keep[i].name)); |
|
|
|
env_keep[i].value = estrdup(getenv(env_keep[i].name)); |
|
|
|
if (!env_keep[i].value) |
|
|
|
|
|
|
|
die("strdup:"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
environ = NULL; // in place of clearenv
|
|
|
|
environ = NULL; // in place of clearenv
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -187,20 +272,30 @@ int main(int argc, char *argv[]) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (envflag) { |
|
|
|
for (int i = 0; envflag &&env_keep[i].name; i++) { |
|
|
|
for (int i = 0; env_keep[i].name; i++) { |
|
|
|
if (env_keep[i].value) { |
|
|
|
if (env_keep[i].value) { |
|
|
|
err = setenv(env_keep[i].name, env_keep[i].value, 1); |
|
|
|
err = setenv(env_keep[i].name, env_keep[i].value, 1); |
|
|
|
if (err == -1) { |
|
|
|
if (err == -1) { |
|
|
|
fprintf(stderr, "setenv: %s\n", strerror(errno)); |
|
|
|
fprintf(stderr, "setenv: %s\n", strerror(errno)); |
|
|
|
goto fail_end; |
|
|
|
goto fail_end; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; env_extra && env_extra[i].name; i++) { |
|
|
|
|
|
|
|
if (env_extra[i].value) { |
|
|
|
|
|
|
|
err = setenv(env_extra[i].name, env_extra[i].value, 1); |
|
|
|
|
|
|
|
if (err == -1) { |
|
|
|
|
|
|
|
fprintf(stderr, "setenv: %s\n", strerror(errno)); |
|
|
|
|
|
|
|
goto fail_end; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
unsetenv(env_extra[i].name); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// do not override, we might be under more levels of 'us'
|
|
|
|
// do not override, we might be under more levels of 'us'
|
|
|
|
err = setenv("US_USER", my_name, 0); |
|
|
|
err = setenv("US_USER", my_name, 0); |
|
|
|
free(my_name); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0; |
|
|
|
errno = 0; |
|
|
|
/* Set permissions */ |
|
|
|
/* Set permissions */ |
|
|
@ -233,7 +328,7 @@ static inline void usage(void) |
|
|
|
// -c [file]: manually select config file
|
|
|
|
// -c [file]: manually select config file
|
|
|
|
// something about environment
|
|
|
|
// something about environment
|
|
|
|
// something about non interactiveness
|
|
|
|
// something about non interactiveness
|
|
|
|
printf("usage: us [-se] [-u user] [-g group] command [args]\n"); |
|
|
|
printf("usage: us [-seA] [-u user] [-g group] [-C config] command [args]\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static int perm_set(struct passwd *pw, struct group *gr) |
|
|
|
static int perm_set(struct passwd *pw, struct group *gr) |
|
|
@ -285,9 +380,6 @@ static int authenticate(uid_t uid, char ask) |
|
|
|
char *p = pw->pw_passwd; |
|
|
|
char *p = pw->pw_passwd; |
|
|
|
int tty_fd = STDOUT_FILENO; |
|
|
|
int tty_fd = STDOUT_FILENO; |
|
|
|
|
|
|
|
|
|
|
|
// but we have to be root
|
|
|
|
|
|
|
|
if (setuid(0) == -1) |
|
|
|
|
|
|
|
die("setreuid:"); |
|
|
|
|
|
|
|
if (!strcmp(p, "x") || *p == '*' || *p == '!') { |
|
|
|
if (!strcmp(p, "x") || *p == '*' || *p == '!') { |
|
|
|
#if defined(__linux__) |
|
|
|
#if defined(__linux__) |
|
|
|
// get exclusive access to shadow
|
|
|
|
// get exclusive access to shadow
|
|
|
@ -300,10 +392,7 @@ static int authenticate(uid_t uid, char ask) |
|
|
|
hash_p = sp->sp_pwdp; |
|
|
|
hash_p = sp->sp_pwdp; |
|
|
|
endspent(); |
|
|
|
endspent(); |
|
|
|
ulckpwdf(); |
|
|
|
ulckpwdf(); |
|
|
|
// if (setuid(uid) == -1)
|
|
|
|
|
|
|
|
// die("setreuid:");
|
|
|
|
|
|
|
|
#elif defined(__OpenBSD__) |
|
|
|
#elif defined(__OpenBSD__) |
|
|
|
// TODO: openbsd has getpwuid_passwd
|
|
|
|
|
|
|
|
struct passwd *op = getpwuid_shadow(uid); |
|
|
|
struct passwd *op = getpwuid_shadow(uid); |
|
|
|
if (!op) |
|
|
|
if (!op) |
|
|
|
die("getpwuid_shadow:"); |
|
|
|
die("getpwuid_shadow:"); |
|
|
@ -322,7 +411,7 @@ static int authenticate(uid_t uid, char ask) |
|
|
|
char pass[1024] = {0}; |
|
|
|
char pass[1024] = {0}; |
|
|
|
struct termios tio_before, tio_pass; |
|
|
|
struct termios tio_before, tio_pass; |
|
|
|
if (ask && askpass) { |
|
|
|
if (ask && askpass) { |
|
|
|
pid_t pid; |
|
|
|
pid_t pid, parent = getpid(); |
|
|
|
int pipefd[2]; |
|
|
|
int pipefd[2]; |
|
|
|
if (pipe(pipefd) == -1) |
|
|
|
if (pipe(pipefd) == -1) |
|
|
|
die("pipe:"); |
|
|
|
die("pipe:"); |
|
|
@ -331,23 +420,28 @@ static int authenticate(uid_t uid, char ask) |
|
|
|
die("fork:"); |
|
|
|
die("fork:"); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case 0: |
|
|
|
case 0: |
|
|
|
|
|
|
|
/* we are still root, drop off permissions before
|
|
|
|
|
|
|
|
* disasters happen */ |
|
|
|
|
|
|
|
if (setuid(uid) == -1) { |
|
|
|
|
|
|
|
kill(parent, SIGTERM); |
|
|
|
|
|
|
|
die("askpass: setuid:"); |
|
|
|
|
|
|
|
} |
|
|
|
if (dup2(pipefd[1], STDOUT_FILENO) == -1) { |
|
|
|
if (dup2(pipefd[1], STDOUT_FILENO) == -1) { |
|
|
|
// TODO: send signal to parent dup failed
|
|
|
|
kill(parent, SIGTERM); |
|
|
|
die("dup2:"); |
|
|
|
die("askpass: dup2:"); |
|
|
|
} |
|
|
|
} |
|
|
|
close(pipefd[0]); |
|
|
|
close(pipefd[0]); |
|
|
|
execl(askpass, "", (char *)NULL); |
|
|
|
execl("/bin/sh", "sh", "-c", askpass, (char *)NULL); |
|
|
|
die("execl:"); |
|
|
|
kill(parent, SIGTERM); |
|
|
|
|
|
|
|
die("askpass: execl:"); |
|
|
|
break; |
|
|
|
break; |
|
|
|
default: |
|
|
|
default: |
|
|
|
fd = pipefd[0]; |
|
|
|
fd = pipefd[0]; |
|
|
|
close(pipefd[1]); |
|
|
|
close(pipefd[1]); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} else if (ask) { |
|
|
|
|
|
|
|
die("askpass: no valid askpass specified"); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
printf("Password: "); |
|
|
|
printf("Password (%s): ", pw->pw_name); |
|
|
|
fflush(stdout); |
|
|
|
fflush(stdout); |
|
|
|
if (tcgetattr(tty_fd, &tio_before) == -1) |
|
|
|
if (tcgetattr(tty_fd, &tio_before) == -1) |
|
|
|
die("tcgetattr:"); |
|
|
|
die("tcgetattr:"); |
|
|
@ -374,7 +468,7 @@ static int authenticate(uid_t uid, char ask) |
|
|
|
if (pass[l-1] == '\n') |
|
|
|
if (pass[l-1] == '\n') |
|
|
|
pass[--l] = '\0'; |
|
|
|
pass[--l] = '\0'; |
|
|
|
|
|
|
|
|
|
|
|
if (ask) { |
|
|
|
if (ask && askpass) { |
|
|
|
close(fd); |
|
|
|
close(fd); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
if (tcsetattr(tty_fd, TCSANOW, &tio_before) == -1) |
|
|
|
if (tcsetattr(tty_fd, TCSANOW, &tio_before) == -1) |
|
|
@ -382,6 +476,7 @@ static int authenticate(uid_t uid, char ask) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
char *enc = crypt(pass, hash); |
|
|
|
char *enc = crypt(pass, hash); |
|
|
|
|
|
|
|
/* clean pass from memory */ |
|
|
|
memset(pass, 0, 1024); |
|
|
|
memset(pass, 0, 1024); |
|
|
|
if (strncmp(hash, enc, 1024)) { |
|
|
|
if (strncmp(hash, enc, 1024)) { |
|
|
|
printf("Authentication failure\n"); |
|
|
|
printf("Authentication failure\n"); |
|
|
@ -399,7 +494,7 @@ static struct passwd* user_to_passwd(const char *user) |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct passwd* pw; |
|
|
|
struct passwd *pw, *pr; |
|
|
|
long uid_l; |
|
|
|
long uid_l; |
|
|
|
errno = 0; |
|
|
|
errno = 0; |
|
|
|
if (user[0] != '#') { |
|
|
|
if (user[0] != '#') { |
|
|
@ -418,8 +513,9 @@ static struct passwd* user_to_passwd(const char *user) |
|
|
|
errno = EINVAL; |
|
|
|
errno = EINVAL; |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
pr = emalloc(sizeof(struct passwd)); |
|
|
|
return pw; |
|
|
|
memcpy(pr, pw, sizeof(struct passwd)); |
|
|
|
|
|
|
|
return pr; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static struct group* group_to_grp(const char *group) |
|
|
|
static struct group* group_to_grp(const char *group) |
|
|
@ -429,7 +525,7 @@ static struct group* group_to_grp(const char *group) |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
struct group* gr; |
|
|
|
struct group *gr, *rr; |
|
|
|
long gid_l; |
|
|
|
long gid_l; |
|
|
|
errno = 0; |
|
|
|
errno = 0; |
|
|
|
if (group[0] != '#') { |
|
|
|
if (group[0] != '#') { |
|
|
@ -448,7 +544,9 @@ static struct group* group_to_grp(const char *group) |
|
|
|
errno = EINVAL; |
|
|
|
errno = EINVAL; |
|
|
|
return NULL; |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
return gr; |
|
|
|
rr = emalloc(sizeof(struct group)); |
|
|
|
|
|
|
|
memcpy(rr, gr, sizeof(struct group)); |
|
|
|
|
|
|
|
return rr; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void die(const char *fmt, ...) |
|
|
|
void die(const char *fmt, ...) |
|
|
@ -469,3 +567,128 @@ void die(const char *fmt, ...) |
|
|
|
exit(errno ? errno : EXIT_FAILURE); |
|
|
|
exit(errno ? errno : EXIT_FAILURE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *emalloc(size_t s) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!s || s == (size_t)-1) |
|
|
|
|
|
|
|
die("bad malloc: invalid size"); |
|
|
|
|
|
|
|
void *p = calloc(1, s); |
|
|
|
|
|
|
|
if (!p) |
|
|
|
|
|
|
|
die("bad malloc:"); |
|
|
|
|
|
|
|
return p; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void *erealloc(void *p, size_t s) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!s || s == (size_t)-1) |
|
|
|
|
|
|
|
die("bad realloc: invalid size"); |
|
|
|
|
|
|
|
void *r = realloc(p, s); |
|
|
|
|
|
|
|
if (!r) |
|
|
|
|
|
|
|
die("bad realloc:"); |
|
|
|
|
|
|
|
return r; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *estrdup(const char *s) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!s) |
|
|
|
|
|
|
|
die("bad strdup: cannot duplicate NULL pointer"); |
|
|
|
|
|
|
|
char *r = strdup(s); |
|
|
|
|
|
|
|
if (!r) |
|
|
|
|
|
|
|
die("bad strdup:"); |
|
|
|
|
|
|
|
return r; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int get_config(struct config **conf, int *num) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (!conf || !num) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
FILE *fp = fopen(config_file, "r"); |
|
|
|
|
|
|
|
if (!fp) |
|
|
|
|
|
|
|
die("fopen:"); |
|
|
|
|
|
|
|
struct stat st; |
|
|
|
|
|
|
|
if (fstat(fileno(fp), &st) == -1) |
|
|
|
|
|
|
|
die("fstat:"); |
|
|
|
|
|
|
|
if (st.st_uid != 0 || st.st_gid != 0) |
|
|
|
|
|
|
|
die("config file must be owned by root:root"); |
|
|
|
|
|
|
|
if (!S_ISREG(st.st_mode)) |
|
|
|
|
|
|
|
die("config file must be a regular file"); |
|
|
|
|
|
|
|
if (st.st_mode & S_IRWXO || st.st_mode & S_IROTH || |
|
|
|
|
|
|
|
st.st_mode & S_IWOTH || st.st_mode & S_IXOTH) |
|
|
|
|
|
|
|
die("others may not modify, read or execute config file"); |
|
|
|
|
|
|
|
char line[CONF_LINE_MAX]; |
|
|
|
|
|
|
|
*num = 0; |
|
|
|
|
|
|
|
*conf = NULL; |
|
|
|
|
|
|
|
for (int i = 0; fgets(line, CONF_LINE_MAX, fp); i++) { |
|
|
|
|
|
|
|
char *s, *t, *sv; |
|
|
|
|
|
|
|
int n = 0; |
|
|
|
|
|
|
|
for (int ll = strlen(line)-1; isspace(line[ll]); line[ll--] = '\0'); |
|
|
|
|
|
|
|
struct config c = {0}; |
|
|
|
|
|
|
|
for (s = line;; s = NULL, n++) { |
|
|
|
|
|
|
|
int getflags = 0; |
|
|
|
|
|
|
|
if (!(t = strtok_r(s, " ", &sv))) |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
if (*t == '#') |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
switch (n) { |
|
|
|
|
|
|
|
case 0: |
|
|
|
|
|
|
|
if (!strcmp(t, "+")) |
|
|
|
|
|
|
|
c.type = 1; |
|
|
|
|
|
|
|
else if (!strcmp(t, "-")) |
|
|
|
|
|
|
|
c.type = 0; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
die("non valid config line %d", i); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 1: |
|
|
|
|
|
|
|
c.who = estrdup(t); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 2: |
|
|
|
|
|
|
|
if (strcmp(t, "as")) |
|
|
|
|
|
|
|
die("non valid config line %d", i); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 3: |
|
|
|
|
|
|
|
c.as = estrdup(t); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
getflags = 1; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!getflags) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isupper(*t)) { |
|
|
|
|
|
|
|
char *e, *se, *et; |
|
|
|
|
|
|
|
for (e = t;; e = NULL) { |
|
|
|
|
|
|
|
if (!(et = strtok_r(e, ",", &se))) |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
char *sep; |
|
|
|
|
|
|
|
int x; |
|
|
|
|
|
|
|
if (!(sep = strchr(et, ','))) |
|
|
|
|
|
|
|
die("invalid env at %d", i); |
|
|
|
|
|
|
|
c.env = erealloc(c.env, (c.env_n+1)*sizeof(struct env_elem)); |
|
|
|
|
|
|
|
x = c.env_n; |
|
|
|
|
|
|
|
*sep = '\0'; |
|
|
|
|
|
|
|
for (char *p = et; *p; p++) |
|
|
|
|
|
|
|
if (!isupper(*p) && *p != '-' |
|
|
|
|
|
|
|
&& *p != '_') |
|
|
|
|
|
|
|
die("non valid" |
|
|
|
|
|
|
|
"env at %d", i); |
|
|
|
|
|
|
|
c.env[x].name = estrdup(et); |
|
|
|
|
|
|
|
c.env[x].value = estrdup(sep+1); |
|
|
|
|
|
|
|
c.env_n++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if (!strcmp(t, "persist")) { |
|
|
|
|
|
|
|
c.flags |= FLAG_PERSIST; |
|
|
|
|
|
|
|
} else if (!strcmp(t, "nopass")) { |
|
|
|
|
|
|
|
c.flags |= FLAG_NOPASS; |
|
|
|
|
|
|
|
} else if (!strcmp(t, "nolog")) { |
|
|
|
|
|
|
|
c.flags |= FLAG_NOLOG; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (n < 3) |
|
|
|
|
|
|
|
die("non valid config line %d", i); |
|
|
|
|
|
|
|
*conf = erealloc(*conf, ((*num)+1)*sizeof(struct config)); |
|
|
|
|
|
|
|
(*conf)[(*num)] = c; |
|
|
|
|
|
|
|
*num += 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fclose(fp); |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
} |
|
|
|