|
|
|
@ -25,6 +25,7 @@ |
|
|
|
|
|
|
|
|
|
#include <sys/types.h> |
|
|
|
|
#include <sys/wait.h> |
|
|
|
|
#include <sys/stat.h> |
|
|
|
|
|
|
|
|
|
#include <stdio.h> |
|
|
|
|
#include <stdlib.h> |
|
|
|
@ -36,6 +37,7 @@ |
|
|
|
|
#include <grp.h> |
|
|
|
|
#include <termios.h> |
|
|
|
|
#include <stdarg.h> |
|
|
|
|
#include <ctype.h> |
|
|
|
|
|
|
|
|
|
#if !defined(_XOPEN_CRYPT) || _XOPEN_CRYPT == -1 |
|
|
|
|
#include <crypt.h> |
|
|
|
@ -46,16 +48,37 @@ |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#define MAX_HASH 1024 |
|
|
|
|
#define CONF_FILE "/etc/us.conf" |
|
|
|
|
#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 die(const char *, ...); |
|
|
|
|
static int perm_set(struct passwd *, struct group *); |
|
|
|
|
static int authenticate(uid_t, char); |
|
|
|
|
static struct passwd* user_to_passwd(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; |
|
|
|
|
|
|
|
|
@ -104,22 +127,92 @@ int main(int argc, char *argv[]) |
|
|
|
|
fprintf(stderr, "getpwid: %s\n", strerror(errno)); |
|
|
|
|
return errno; |
|
|
|
|
} |
|
|
|
|
char *my_name = estrdup(my_pw->pw_name); |
|
|
|
|
char *my_name = my_pw->pw_name; |
|
|
|
|
gid_t my_groups[GROUPS_MAX]; |
|
|
|
|
int n_groups = 0; |
|
|
|
|
if ((n_groups = getgroups(GROUPS_MAX, my_groups)) == -1) |
|
|
|
|
die("getgroups:"); |
|
|
|
|
|
|
|
|
|
/* Get target user and group info */ |
|
|
|
|
t_pw = user_to_passwd(t_usr); |
|
|
|
|
if (!t_pw) |
|
|
|
|
die("user_to_passwd:"); |
|
|
|
|
t_gr = group_to_grp(t_grp); |
|
|
|
|
|
|
|
|
|
/* 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, 0)) |
|
|
|
|
exit(EXIT_FAILURE); |
|
|
|
|
|
|
|
|
|
/* Get target user and group info */ |
|
|
|
|
t_pw = user_to_passwd(t_usr); |
|
|
|
|
if (!t_pw) |
|
|
|
|
die("usr_to_passwd:"); |
|
|
|
|
t_gr = group_to_grp(t_grp); |
|
|
|
|
|
|
|
|
|
/* Get target user's shell */ |
|
|
|
|
if (!shellflag) |
|
|
|
|
shell = t_pw->pw_shell; |
|
|
|
@ -142,11 +235,6 @@ int main(int argc, char *argv[]) |
|
|
|
|
} |
|
|
|
|
c_argv[c_argc] = NULL; |
|
|
|
|
|
|
|
|
|
struct env_elem { |
|
|
|
|
char *name; |
|
|
|
|
char *value; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
struct env_elem env_keep[] = { |
|
|
|
|
{"PATH", NULL}, |
|
|
|
|
{"TERM", NULL}, |
|
|
|
@ -179,8 +267,7 @@ int main(int argc, char *argv[]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (envflag) { |
|
|
|
|
for (int i = 0; env_keep[i].name; i++) { |
|
|
|
|
for (int i = 0; envflag &&env_keep[i].name; i++) { |
|
|
|
|
if (env_keep[i].value) { |
|
|
|
|
err = setenv(env_keep[i].name, env_keep[i].value, 1); |
|
|
|
|
if (err == -1) { |
|
|
|
@ -189,10 +276,21 @@ int main(int argc, char *argv[]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
err = setenv("US_USER", my_name, 0); |
|
|
|
|
free(my_name); |
|
|
|
|
|
|
|
|
|
errno = 0; |
|
|
|
|
/* Set permissions */ |
|
|
|
@ -386,7 +484,7 @@ static struct passwd* user_to_passwd(const char *user) |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct passwd* pw; |
|
|
|
|
struct passwd *pw, *pr; |
|
|
|
|
long uid_l; |
|
|
|
|
errno = 0; |
|
|
|
|
if (user[0] != '#') { |
|
|
|
@ -405,8 +503,9 @@ static struct passwd* user_to_passwd(const char *user) |
|
|
|
|
errno = EINVAL; |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return pw; |
|
|
|
|
pr = emalloc(sizeof(struct passwd)); |
|
|
|
|
memcpy(pr, pw, sizeof(struct passwd)); |
|
|
|
|
return pr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static struct group* group_to_grp(const char *group) |
|
|
|
@ -416,7 +515,7 @@ static struct group* group_to_grp(const char *group) |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct group* gr; |
|
|
|
|
struct group *gr, *rr; |
|
|
|
|
long gid_l; |
|
|
|
|
errno = 0; |
|
|
|
|
if (group[0] != '#') { |
|
|
|
@ -435,7 +534,9 @@ static struct group* group_to_grp(const char *group) |
|
|
|
|
errno = EINVAL; |
|
|
|
|
return NULL; |
|
|
|
|
} |
|
|
|
|
return gr; |
|
|
|
|
rr = emalloc(sizeof(struct group)); |
|
|
|
|
memcpy(rr, gr, sizeof(struct group)); |
|
|
|
|
return rr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void die(const char *fmt, ...) |
|
|
|
@ -460,12 +561,22 @@ void *emalloc(size_t s) |
|
|
|
|
{ |
|
|
|
|
if (!s || s == (size_t)-1) |
|
|
|
|
die("bad malloc: invalid size"); |
|
|
|
|
void *p = malloc(s); |
|
|
|
|
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) |
|
|
|
@ -475,3 +586,99 @@ char *estrdup(const char *s) |
|
|
|
|
die("bad strdup:"); |
|
|
|
|
return r; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int get_config(struct config **conf, int *num) |
|
|
|
|
{ |
|
|
|
|
if (!conf || !num) |
|
|
|
|
return -1; |
|
|
|
|
FILE *fp = fopen(CONF_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; |
|
|
|
|
} |
|
|
|
|