diff --git a/.gitignore b/.gitignore index 0331bbb..f94b99a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# binary +fakeadd + # Object files *.o diff --git a/fakeadd.c b/fakeadd.c new file mode 100644 index 0000000..136fa93 --- /dev/null +++ b/fakeadd.c @@ -0,0 +1,172 @@ +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +static char *name; + +void usage_fd(FILE * f) { + fprintf(f,"Usage: %s -G|-U -n name [-g gid] [-u uid] [-p password] [-m memberlist] [-s shell] [-c gecos] [-d dir] [-h] \n" + "(C) 2013 ProgAndy\n", + basename(name)); +} + +void usage(void) { + usage_fd(stdout); +} + +void help() { + puts("fakeadd is a tool to add users groups to a fakeuser environment.\n" + "fakeuser works best in conjunction with fakeroot. Use it with LD_PRELOAD.\n"); + usage(); + puts("\nPARAMETER:\n" + "\n -U : ADD USER\n" + " -n name - Username\n" + " -u uid - User ID (optional, default 0)\n" + " -g gid - Group ID (optional, default 0)\n" + " -p password - Password (optional, default empty)\n" + " -s shell - Shell (optional, default empty) Shell\n" + " -c gecos - Real name (optional, default empty)\n" + " -d dir - Home directory (optional, default empty)\n" + "\n -G : ADD GROUP\n" + " -n name - Groupname\n" + " -g gid - Group ID (optional, default 0)\n" + " -p password - Password (optional, default empty) \n" + " -m memberlist - Memberlist (optional, default empty)\n" + " [Example: username,exampleuser,nobody]" + ); +} + + +char **string_to_array(const char *s, const char *delims, char **strings) +{ + char ** array = malloc( (strlen(s)/2 + 2) * sizeof(char*) ); + char *str = strdup(s); + if (strings) *strings = str; + int len = 0; + + char *tok = strtok(str, delims); + while (tok) { + array[len++] = tok; + tok = strtok(NULL, delims); + } + array[len++] = NULL; + array = (char**) realloc(array, len * sizeof(char*)); + return array; +} + + + + +int main(int argc, char **argv) { + char * tmpdir = getenv("_FAKEUSER_DIR_"); + char *passwd_file, *group_file; + + name = argv[0]; + int opt, ret = 0; + int action = 0, uid = 0, gid = 0; + char *name = NULL, *passwd = NULL, *members = NULL, *shell = NULL, *gecos = NULL, *dir = NULL; + extern char *optarg; + + while ((opt = getopt(argc, argv, "UGu:g:n:p:m:s:c:d:h")) != -1) { + switch (opt) { + case 'U': + action = 'U'; + break; + case 'G': + action = 'G'; + break; + case 'u': + uid = atoi(optarg); + break; + case 'g': + gid = atoi(optarg); + break; + case 'n': + name = optarg; + break; + case 'p': + passwd = optarg; + break; + case 'm': + members = optarg; + break; + case 's': + shell = optarg; + break; + case 'c': + gecos = optarg; + break; + case 'd': + dir = optarg; + break; + case 'h': + help(); + exit(EXIT_SUCCESS); + default: /* '?' */ + usage_fd(stderr); + exit(EXIT_FAILURE); + } + } + if (action == 0 || name == NULL) { + usage(); + exit(EXIT_FAILURE); + } + + if (!tmpdir) { + fputs("Error! Not in fakeuser environment\n", stderr); + exit(EXIT_FAILURE); + } + passwd_file = (char*)malloc(strlen(tmpdir)+10); + group_file = (char*)malloc(strlen(tmpdir)+10); + strcpy(passwd_file, tmpdir); + strcpy(group_file, tmpdir); + strcat(passwd_file, "/passwd"); + strcat(group_file, "/group"); + + // Create directory structure + mkdir_r(tmpdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + if (action == 'U') { + struct passwd pw; + pw.pw_name = name; + pw.pw_passwd = passwd ? passwd : ""; + pw.pw_gecos = gecos ? gecos : ""; + pw.pw_dir = dir ? dir : ""; + pw.pw_shell = shell ? shell : ""; + pw.pw_uid = uid; + pw.pw_gid = gid; + FILE * pwf = fopen(passwd_file, "a"); + if (pwf) { + if(putpwent(&pw, pwf)) + ret = EIO; + if (fclose(pwf)) + ret = EIO; + } else + ret = EIO; + } else if (action == 'G') { + struct group gr; + gr.gr_name = name; + gr.gr_passwd = passwd ? passwd : ""; + gr.gr_gid = gid; + char *strings; + gr.gr_mem = members ? string_to_array(members, " ,;", &strings) : (char *[]){NULL}; + FILE * pwf = fopen(group_file, "a"); + if (pwf) { + if(putgrent(&gr, pwf)) + ret = EIO; + if (fclose(pwf)) + ret = EIO; + } else + ret = EIO; + //return fakeaddgroup(&gr); + } + return ret; +} \ No newline at end of file diff --git a/fakeuser.c b/fakeuser.c new file mode 100644 index 0000000..9e35bc2 --- /dev/null +++ b/fakeuser.c @@ -0,0 +1,274 @@ +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + + +static void fakeuser_init(void) __attribute__ ((constructor)); +static void fakeuser_uninit(void) __attribute__ ((destructor)); + + +#define TMPROOT "/tmp/fakeuser" +static int fake_owner = 0; +static char tmpdirtmpl[] = TMPROOT "/XXXXXX"; +static char *tmpdir, *passwd_file, *group_file; + +FILE *pwstream = NULL; +FILE *grstream = NULL; + +void setpwent (void) { + void (*o_setpwent)(void) = NULL; + if (!o_setpwent) o_setpwent = dlsym(RTLD_NEXT, "setpwent"); + if (!pwstream) + pwstream = fopen(passwd_file, "r"); + else + rewind(pwstream); + o_setpwent(); +} + +void endpwent (void) { + void (*o_endpwent)(void) = NULL; + if (!o_endpwent) o_endpwent = dlsym(RTLD_NEXT, "endpwent"); + if (pwstream) { + fclose(pwstream); + pwstream = NULL; + } + o_endpwent(); +} + +struct passwd *getpwent (void) { + struct passwd *(*o_getpwent)(void) = NULL; + if (!pwstream) + pwstream = fopen(passwd_file, "r"); + if (pwstream) { + struct passwd *pw = fgetpwent(pwstream); + if (pw) return pw; + } + if (!o_getpwent) o_getpwent = dlsym(RTLD_NEXT, "getpwent"); + return o_getpwent(); +} + +struct passwd *getpwnam (const char *__name) { + struct passwd *(*o_getpwnam)(const char *__name) = NULL; + + struct passwd *pw; + FILE *pwf = fopen(passwd_file, "r"); + if (pwf) { + do { + pw = fgetpwent(pwf); + if (pw && !strcmp(pw->pw_name, __name)) + break; + } while (pw); + fclose(pwf); + if (pw && !strcmp(pw->pw_name, __name)) + return pw; + } + + if (!o_getpwnam) o_getpwnam = dlsym(RTLD_NEXT, "getpwnam"); + return o_getpwnam(__name); +} + +struct passwd *getpwuid (__uid_t __uid) { + struct passwd *(*o_getpwuid)(__uid_t __uid) = NULL; + + struct passwd *pw; + FILE *pwf = fopen(passwd_file, "r"); + if (pwf) { + do { + pw = fgetpwent(pwf); + if (pw && pw->pw_uid == __uid) + break; + } while (pw); + fclose(pwf); + if (pw && pw->pw_uid == __uid) + return pw; + } + + if (!o_getpwuid) o_getpwuid = dlsym(RTLD_NEXT, "getpwuid"); + return o_getpwuid(__uid); +} + +int getpwuid_r (__uid_t __uid, struct passwd *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct passwd **__restrict __result) { + int (*o_getpwuid_r)(__uid_t, struct passwd *, char*, size_t, struct passwd**) = NULL; + if (__resultbuf == NULL || __result == NULL) return ENOMEM; + *__result = NULL; + + int res; + FILE *pwf = fopen(passwd_file, "r"); + if (pwf) { + do { + res = fgetpwent_r(pwf, __resultbuf, __buffer, __buflen, __result); + if (!res && __resultbuf->pw_uid == __uid) + break; + } while (!res); + fclose(pwf); + if (res == ERANGE) + return ERANGE; + if (!res && __resultbuf->pw_uid == __uid) + return 0; + } + + if (!o_getpwuid_r) o_getpwuid_r = dlsym(RTLD_NEXT, "getpwuid_r"); + return o_getpwuid_r(__uid, __resultbuf, __buffer, __buflen, __result); +} + +// now for groups + + +void setgrent (void) { + void (*o_setgrent)(void) = NULL; + if (!o_setgrent) o_setgrent = dlsym(RTLD_NEXT, "setgrent"); + if (!grstream) + grstream = fopen(group_file, "r"); + else + rewind(grstream); + o_setgrent(); +} + +void endgrent (void) { + void (*o_endgrent)(void) = NULL; + if (!o_endgrent) o_endgrent = dlsym(RTLD_NEXT, "endgrent"); + if (grstream) { + fclose(grstream); + grstream = NULL; + } + o_endgrent(); +} + +struct group *getgrent (void) { + struct group *(*o_getgrent)(void) = NULL; + + if (!grstream) + grstream = fopen(group_file, "r"); + if (grstream) { + struct group *gr = fgetgrent(grstream); + if (gr) return gr; + } + + if (!o_getgrent) o_getgrent = dlsym(RTLD_NEXT, "getgrent"); + return o_getgrent(); +} + +struct group *getgrnam (const char *__name) { + struct group *(*o_getgrnam)(const char *__name) = NULL; + + struct group *gr; + FILE *pwf = fopen(group_file, "r"); + if (pwf) { + do { + gr = fgetgrent(pwf); + if (gr && !strcmp(gr->gr_name, __name)) + break; + } while (gr); + fclose(pwf); + if (gr && !strcmp(gr->gr_name, __name)) + return gr; + } + + + if (!o_getgrnam) o_getgrnam = dlsym(RTLD_NEXT, "getgrnam"); + return o_getgrnam(__name); +} + +struct group *getgrgid (__gid_t __gid) { + struct group *(*o_getgrgid)(__gid_t __gid) = NULL; + + + struct group *gr; + FILE *pwf = fopen(group_file, "r"); + if (pwf) { + do { + gr = fgetgrent(pwf); + if (gr && gr->gr_gid == __gid) + break; + } while (gr); + fclose(pwf); + if (gr && gr->gr_gid == __gid) + return gr; + } + + if (!o_getgrgid) o_getgrgid = dlsym(RTLD_NEXT, "getgrgid"); + return o_getgrgid(__gid); +} + +/* +int getgrent_r (struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result) { + int (*getgrent_r)(struct group *, char*, size_t*, struct group**) = NULL; + + +} +*/ + +int getgrgid_r (__gid_t __gid, struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result) { + int (*o_getgrgid_r)(__gid_t, struct group *, char*, size_t, struct group**) = NULL; + if (__resultbuf == NULL || __result == NULL) return ENOMEM; + + + + int res; + FILE *pwf = fopen(group_file, "r"); + if (pwf) { + do { + res = fgetgrent_r(pwf, __resultbuf, __buffer, __buflen, __result); + if (!res && __resultbuf->gr_gid == __gid) + break; + } while (!res); + fclose(pwf); + if (res == ERANGE) + return ERANGE; + if (!res && __resultbuf->gr_gid == __gid) + return 0; + } + + if (!o_getgrgid_r) o_getgrgid_r = dlsym(RTLD_NEXT, "getgrgid_r"); + return o_getgrgid_r(__gid, __resultbuf, __buffer, __buflen, __result); +} + +/* +extern int getgrnam_r (const char *__restrict __name, + struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result); +*/ + +static void fakeuser_init(void) { + char * dir = getenv("_FAKEUSER_DIR_"); + if (dir) { + tmpdir = dir; + } else { + tmpdir = mktemp(tmpdirtmpl); + fake_owner = 1; + setenv("_FAKEUSER_DIR_", tmpdir, 1); + mkdir_r(tmpdir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + } + passwd_file = (char*)malloc(strlen(tmpdir)+10); + group_file = (char*)malloc(strlen(tmpdir)+10); + strcpy(passwd_file, tmpdir); + strcpy(group_file, tmpdir); + strcat(passwd_file, "/passwd"); + strcat(group_file, "/group"); +} + +static void fakeuser_uninit(void) { + if (fake_owner) { + unlink(passwd_file); + unlink(group_file); + rmdir(tmpdir); + rmdir(TMPROOT); + } +} \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..ad40a90 --- /dev/null +++ b/makefile @@ -0,0 +1,11 @@ +CC = gcc + +CFLAGS += -D_GNU_SOURCE -std=c99 -Wall + +all: libfakeuser.so fakeadd + +libfakeuser.so: fakeuser.c util.c + $(CC) $(CFLAGS) -shared -fPIC -o libfakeuser.so fakeuser.c util.c -ldl $(LDFLAGS) + +fakeadd: fakeadd.c util.c + $(CC) $(CFLAGS) -o fakeadd fakeadd.c util.c -ldl $(LDFLAGS) diff --git a/util.c b/util.c new file mode 100644 index 0000000..213208c --- /dev/null +++ b/util.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include +#include "util.h" + +int mkdir_r(char *dir, mode_t mode) { + char *str = strdup(dir); + char *tok = strtok(str, "/"); + int res = 0, setslash; + while (tok) { + setslash=1; + while (tok != str && *(tok-setslash) == '\0') { + *(tok-setslash) = '/'; + setslash++; + } + if (strlen(str)) { + res = mkdir(str, mode); + if (res && errno != EEXIST) + break; + } + tok = strtok(NULL, "/"); + } + free(str); + return res; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..2b56976 --- /dev/null +++ b/util.h @@ -0,0 +1,8 @@ +#ifndef __FU_UTIL_H__ +#define __FU_UTIL_H__ + +#include + +int mkdir_r(char *dir, mode_t mode); + +#endif //__FU_UTIL_H__ \ No newline at end of file