// // aegis - project change supervisor // Copyright (C) 1991-2008 Peter Miller // // This program 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 3 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, see // . // #include #include #include #include #include #include #include #include #include #include #include static int become_orig_uid; static int become_orig_gid; static int become_orig_umask; #ifdef DEBUG static int become_inited; #endif /** * The become_testing variable is used to remember the testing state of * the program. It assumes one of three values: * * -1 means we are one of the non-set-uid-root programs (e.g. aereport) * 0 means set-uid-root program (e.g. aegis or aeimport) * 1 means Aegis is in testing (not set-uid-root) mode. */ static int become_testing; static int become_active; static int become_active_uid; static int become_active_gid; static int become_active_umask; void os_setgid(int gid) { os_become_must_not_be_active(); if (become_testing < 0) return; if (setgid(gid)) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_long(scp, "Argument", gid); if (become_testing) { // // don't use os_testing_mode(), // it could mess with errno // error_intl(scp, i18n("warning: setgid $arg: $errno")); } else fatal_intl(scp, i18n("setgid $arg: $errno")); sub_context_delete(scp); } #if defined(DEBUG) && defined(__linux__) && defined(HAVE_PRCTL) if (!prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif } void os_chown_check(string_ty *path, int mode, int uid, int gid) { struct stat st; int nerrs; int oret; trace(("os_chown_check(path = \"%s\")\n{\n", path->str_text)); os_become_must_be_active(); nerrs = 0; #ifdef S_IFLNK oret = glue_lstat(path->str_text, &st); #else oret = glue_stat(path->str_text, &st); #endif if (oret) { sub_context_ty *scp; int errno_old; errno_old = errno; scp = sub_context_new(); sub_errno_setx(scp, errno_old); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("stat $filename: $errno")); // NOTREACHED } if (mode > 0 && ((int)st.st_mode & 07777) != mode) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "File_Name", path); sub_var_set_format ( scp, "Number1", "%5.5o", (unsigned int)(st.st_mode & 07777) ); sub_var_set_format(scp, "Number2", "%5.5o", mode); error_intl ( scp, i18n("$filename: mode is $number1, should be $number2") ); sub_context_delete(scp); ++nerrs; } if (become_testing > 0) { // // This is the test performed by aegis // in testing (not set-uid-root) mode. // if (st.st_uid != geteuid()) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "File_Name", path); sub_var_set_long(scp, "Number1", st.st_uid); sub_var_set_long(scp, "Number2", geteuid()); error_intl (scp, i18n("$filename: owner is $number1, should be $number2")); sub_context_delete(scp); ++nerrs; } } else if (become_testing == 0) { // // This is the test performed by aegis // when is set-uid-root mode. // if ((int)st.st_uid != uid) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "File_Name", path); sub_var_set_long(scp, "Number1", st.st_uid); sub_var_set_long(scp, "Number2", uid); error_intl ( scp, i18n("$filename: owner is $number1, should be $number2") ); sub_context_delete(scp); ++nerrs; } if (gid >= 0 && (int)st.st_gid != gid) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "File_Name", path); sub_var_set_long(scp, "Number1", st.st_gid); sub_var_set_long(scp, "Number2", gid); error_intl ( scp, i18n("$filename: group is $number1, should be $number2") ); sub_context_delete(scp); ++nerrs; } } else { // // No test is performed by aereport, aefind, etc. // This is because testing mode and non-testing mode // are indistinguishable. // } if (nerrs) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "File_Name", path); fatal_intl(scp, i18n("$filename: has been tampered with (fatal)")); // NOTREACHED } trace(("}\n")); } void os_become_init(void) { assert(!become_inited); become_active_umask = DEFAULT_UMASK; become_orig_umask = umask(DEFAULT_UMASK); become_orig_uid = getuid(); become_orig_gid = getgid(); #ifdef SINGLE_USER become_testing = -1; become_active_uid = become_orig_uid; become_active_gid = become_orig_gid; #else if (setuid(0)) { become_testing = 1; become_active_uid = become_orig_uid; become_active_gid = become_orig_gid; } else { setgid(0); #if defined (DEBUG) && defined (__linux__) if (!prctl (PR_GET_DUMPABLE, 0, 0, 0, 0)) { prctl (PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif } #endif #ifdef DEBUG become_inited = 1; #endif } void os_become_init_mortal(void) { assert(!become_inited); become_active_umask = DEFAULT_UMASK; become_orig_umask = umask(DEFAULT_UMASK); become_orig_uid = getuid(); become_orig_gid = getgid(); become_testing = -1; become_active_uid = become_orig_uid; become_active_gid = become_orig_gid; #ifdef DEBUG become_inited = 1; #endif } void os_become_reinit_mortal(void) { assert(become_inited); become_active_umask = DEFAULT_UMASK; become_orig_umask = umask(DEFAULT_UMASK); become_orig_uid = getuid(); become_orig_gid = getgid(); become_testing = -1; become_active_uid = become_orig_uid; become_active_gid = become_orig_gid; #ifdef DEBUG become_inited = 1; #endif } int os_testing_mode(void) { static int warned; assert(become_inited); if (become_testing <= 0) return 0; if (!warned) { sub_context_ty *scp; warned = 1; scp = sub_context_new(); verbose_intl(scp, i18n("warning: test mode")); sub_context_delete(scp); } return 1; } void os_become(int uid, int gid, int um) { trace(("os_become(uid = %d, gid = %d, um = %03o)\n{\n", uid, gid, um)); if (become_active) fatal_raw("multiple user permissions set (bug)"); become_active = 1; if (os_testing_mode()) { become_active_umask = um; umask(um); } else if (become_testing < 0) { // do nothing if we are mortal become_active_umask = um; umask(um); } else { become_active_uid = uid; become_active_gid = gid; become_active_umask = um; #ifndef CONF_NO_seteuid if (setegid(gid)) nfatal("setegid(%d)", gid); if (seteuid(uid)) nfatal("seteuid(%d)", uid); #if defined (DEBUG) && defined (__linux__) if (!prctl (PR_GET_DUMPABLE, 0, 0, 0, 0)) { prctl (PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif umask(um); #endif } trace(("}\n")); } void os_become_undo(void) { trace(("os_become_undo()\n{\n")); os_become_must_be_active(); os_become_undo_atexit(); trace(("}\n")); } void os_become_undo_atexit(void) { trace(("os_become_undo_atexit()\n{\n")); assert(become_inited); if (become_active) { become_active = 0; if (!become_testing) { #ifndef CONF_NO_seteuid if (seteuid(0)) nfatal("seteuid(0)"); if (setegid(0)) nfatal("setegid(0)"); #if defined(DEBUG) && defined(__linux__) if (!prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif #endif become_active_uid = 0; become_active_gid = 0; } } trace(("}\n")); } void os_become_undo(int uid, int gid) { trace(("os_become_undo(uid = %d, gid = %d)\n{\n", uid, gid)); assert(become_inited); assert(become_active); become_active = 0; if (!become_testing) { assert(become_active_uid == uid); assert(become_active_gid == gid); (void)uid; (void)gid; #ifndef CONF_NO_seteuid if (seteuid(0)) nfatal("seteuid(0)"); if (setegid(0)) nfatal("setegid(0)"); #if defined(DEBUG) && defined(__linux__) if (!prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); } #endif #endif become_active_uid = 0; become_active_gid = 0; } trace(("}\n")); } void os_become_orig(void) { os_become(become_orig_uid, become_orig_gid, become_orig_umask); } void os_become_query(int *uid, int *gid, int *umsk) { os_become_must_be_active(); *uid = become_active_uid; if (gid) *gid = become_active_gid; if (umsk) *umsk = become_active_umask; } void os_become_orig_query(int *uid, int *gid, int *umsk) { if (uid) *uid = become_orig_uid; if (gid) *gid = become_orig_gid; if (umsk) *umsk = become_orig_umask; } int os_become_active(void) { return become_active; } void os_become_must_be_active_gizzards(const char *file, int line) { if (!os_become_active()) fatal_raw("%s: %d: user permissions not set (bug)", file, line); } void os_become_must_not_be_active_gizzards(const char *file, int line) { if (os_become_active()) { fatal_raw ( "%s: %d: user permissions set when aught not (bug)", file, line ); } }