Annotation of suX/src/sux.c, revision 1.7

1.1       misho       1: /*************************************************************************
                      2:  * (C) 2011 AITNET - Sofia/Bulgaria - <office@aitbg.com>
                      3:  *  by Michael Pounov <misho@aitbg.com>
                      4:  *
                      5:  * $Author: misho $
1.7     ! misho       6:  * $Id: sux.c,v 1.6.2.1 2015/06/18 22:15:47 misho Exp $
1.1       misho       7:  *
1.7     ! misho       8:  *************************************************************************
        !             9: The ELWIX and AITNET software is distributed under the following
        !            10: terms:
        !            11: 
        !            12: All of the documentation and software included in the ELWIX and AITNET
        !            13: Releases is copyrighted by ELWIX - Sofia/Bulgaria <info@elwix.org>
        !            14: 
        !            15: Copyright 2004 - 2015
        !            16:        by Michael Pounov <misho@elwix.org>.  All rights reserved.
        !            17: 
        !            18: Redistribution and use in source and binary forms, with or without
        !            19: modification, are permitted provided that the following conditions
        !            20: are met:
        !            21: 1. Redistributions of source code must retain the above copyright
        !            22:    notice, this list of conditions and the following disclaimer.
        !            23: 2. Redistributions in binary form must reproduce the above copyright
        !            24:    notice, this list of conditions and the following disclaimer in the
        !            25:    documentation and/or other materials provided with the distribution.
        !            26: 3. All advertising materials mentioning features or use of this software
        !            27:    must display the following acknowledgement:
        !            28: This product includes software developed by Michael Pounov <misho@elwix.org>
        !            29: ELWIX - Embedded LightWeight unIX and its contributors.
        !            30: 4. Neither the name of AITNET nor the names of its contributors
        !            31:    may be used to endorse or promote products derived from this software
        !            32:    without specific prior written permission.
        !            33: 
        !            34: THIS SOFTWARE IS PROVIDED BY AITNET AND CONTRIBUTORS ``AS IS'' AND
        !            35: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            36: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            37: ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            38: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            39: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            40: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            41: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            42: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            43: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            44: SUCH DAMAGE.
        !            45: */
1.1       misho      46: #include "global.h"
                     47: 
                     48: 
1.4       misho      49: cfg_root_t cfg;
1.1       misho      50: int Verbose;
                     51: struct tagProc proc;
1.2       misho      52: FILE *lf;
1.1       misho      53: 
                     54: 
                     55: static inline void
                     56: Log(int lvl, const char *fmt, ...)
                     57: {
1.2       misho      58:        va_list lst, cp;
1.1       misho      59: 
1.4       misho      60:        EVERBS(lvl) {
1.1       misho      61:                va_start(lst, fmt);
1.2       misho      62:                va_copy(cp, lst);
                     63:                vfprintf(lf, fmt, lst);
1.1       misho      64:                va_end(lst);
1.2       misho      65:                fprintf(lf, "\n");
1.6       misho      66:                vsyslog(LOG_INFO, fmt, cp);
1.2       misho      67:                va_end(cp);
1.1       misho      68:        }
                     69: }
                     70: 
                     71: static inline void
                     72: Err(const char *fmt, ...)
                     73: {
1.2       misho      74:        va_list lst, cp;
1.1       misho      75: 
                     76:        va_start(lst, fmt);
1.2       misho      77:        va_copy(cp, lst);
                     78:        vfprintf(lf, fmt, lst);
1.1       misho      79:        va_end(lst);
1.2       misho      80:        fprintf(lf, "\n");
                     81:        vsyslog(LOG_ERR, fmt, cp);
                     82:        va_end(cp);
1.1       misho      83: }
                     84: 
1.4       misho      85: static inline void
                     86: DumpProc(const char *txt)
                     87: {
1.6       misho      88:        Log(1, "%s:: uid:gid=%d:%d UID:GID=%d:%d Prio=%d Class=%s Name=%s Dir=%s Cmd=%s "
1.5       misho      89:                        "Script=%s From=%s:%s Get=%s", txt ? txt : __func__, 
                     90:                        geteuid(), getegid(), AIT_GET_I16(&proc.proc_uid), 
1.4       misho      91:                        AIT_GET_I16(&proc.proc_gid), AIT_GET_I32(&proc.proc_prio), 
                     92:                        AIT_GET_STR(&proc.proc_class), AIT_GET_STR(&proc.proc_name), 
                     93:                        AIT_GET_STR(&proc.proc_dir), AIT_GET_STR(&proc.proc_cmd), 
                     94:                        getenv("PATH_TRANSLATED"), getenv("REMOTE_ADDR"), 
                     95:                        getenv("REMOTE_PORT"), getenv("REQUEST_URI"));
                     96: }
                     97: 
1.5       misho      98: static int
1.1       misho      99: initProg()
                    100: {
1.4       misho     101:        AIT_SET_I16(&proc.proc_uid, getuid());
                    102:        AIT_SET_I16(&proc.proc_gid, getgid());
                    103:        AIT_SET_I32(&proc.proc_prio, getpriority(PRIO_PROCESS, 0));
                    104:        AIT_INIT_VAL2(&proc.proc_class, string);
1.5       misho     105:        AIT_INIT_VAL2(&proc.proc_dir, string);
1.4       misho     106:        AIT_INIT_VAL2(&proc.proc_name, string);
                    107:        AIT_INIT_VAL2(&proc.proc_cmd, string);
1.1       misho     108: 
1.2       misho     109: #if 0
                    110:        lf = fopen(DEFAULT_LOG, "a");
                    111:        if (!lf)
                    112: #endif
                    113:                lf = stdout;
                    114: 
                    115:        openlog(PACKAGE_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
1.5       misho     116:        return 0;
1.1       misho     117: }
                    118: 
                    119: static void
1.4       misho     120: endProg()
                    121: {
                    122:        AIT_FREE_VAL(&proc.proc_uid);
                    123:        AIT_FREE_VAL(&proc.proc_gid);
                    124:        AIT_FREE_VAL(&proc.proc_prio);
                    125:        AIT_FREE_VAL(&proc.proc_class);
                    126:        AIT_FREE_VAL(&proc.proc_dir);
                    127:        AIT_FREE_VAL(&proc.proc_name);
                    128:        AIT_FREE_VAL(&proc.proc_cmd);
                    129: }
                    130: 
                    131: static void
1.1       misho     132: Usage()
                    133: {
                    134:        printf( " -= suX =- suExecutor designed for web based applicaions\n"
                    135:                "(C)`11 AITNET ltd - Sofia/Bulgaria - <office@aitnet.org>\n\n"
1.6       misho     136:                " Syntax: %s [options] <program|-> [arguments]\n"
1.1       misho     137:                "\t-u <user>\t\t\tUser for suID\n"
                    138:                "\t-g <group>\t\t\tGroup for suID\n"
                    139:                "\t-p <priority (-20..20)>\t\tExecute with priority\n"
                    140:                "\t-d <directory>\t\t\tDirectory for suID\n"
1.3       misho     141:                "\t-C <directory>\t\t\tChroot to directory\n"
                    142:                "\t-c <cfgfile>\t\t\tConfig file\n"
1.2       misho     143:                "\t-l <logfile>\t\t\tLog file path (default:/var/log/suX.log)\n"
1.4       misho     144:                "\t-o\t\t\t\tForce set UID,GID and Priority for program\n"
1.1       misho     145:                "\t-v\t\t\t\tVerbose, (more -v, more verbosity)\n"
                    146:                "\t-h\t\t\t\tThis help screen!\n\n", PACKAGE_NAME);
                    147: }
                    148: 
                    149: static inline int
                    150: setUIDGID(char flg, const char *name)
                    151: {
                    152:        struct stat sb;
1.5       misho     153:        struct passwd *pass;
                    154:        short uid, gid;
1.1       misho     155: 
1.5       misho     156:        if (name) {
                    157:                if (stat(name, &sb) == -1) {
                    158:                        ESYSERR(0);
                    159:                        return -1;
                    160:                }
                    161:                uid = sb.st_uid;
                    162:                gid = sb.st_gid;
                    163:        } else {
1.6       misho     164:                pass = getpwnam(getenv("SUX_USER") ? getenv("SUX_USER") : DEFAULT_SUX_USER);
1.5       misho     165:                if (!pass) {
                    166:                        Err("Error:: User %s not found", getenv("SUX_USER"));
                    167:                        endpwent();
                    168:                        return -1;
                    169:                }
                    170:                uid = pass->pw_uid;
                    171:                gid = pass->pw_gid;
                    172:                endpwent();
1.1       misho     173:        }
                    174: 
1.4       misho     175:        if (!(flg & SUX_GET_UID))
1.5       misho     176:                AIT_SET_I16(&proc.proc_uid, uid);
1.4       misho     177:        if (!(flg & SUX_GET_GID))
1.5       misho     178:                AIT_SET_I16(&proc.proc_gid, gid);
1.1       misho     179: 
                    180:        return 0;
                    181: }
                    182: 
                    183: static inline int
1.5       misho     184: setClassDir()
1.1       misho     185: {
                    186:        struct passwd *pass;
1.4       misho     187:        int ret = 0;
1.1       misho     188: 
1.4       misho     189:        pass = getpwuid(AIT_GET_I16(&proc.proc_uid));
1.1       misho     190:        if (!pass) {
1.4       misho     191:                Err("Error:: User with this UID %d not found", AIT_GET_I16(&proc.proc_uid));
1.5       misho     192:                ret = -1;
                    193:        } else {
1.4       misho     194:                AIT_SET_STR(&proc.proc_class, pass->pw_class);
1.5       misho     195:                AIT_SET_STR(&proc.proc_dir, pass->pw_dir);
1.1       misho     196: 
1.5       misho     197:                if (setusercontext(NULL, pass, AIT_GET_I16(&proc.proc_uid), 
                    198:                                        LOGIN_SETLOGIN | LOGIN_SETGROUP | LOGIN_SETUSER | 
                    199:                                        LOGIN_SETPRIORITY | LOGIN_SETRESOURCES)) {
                    200:                        Err("Error:: Can't set login class %s", AIT_GET_STR(&proc.proc_class));
                    201:                        ret = -1;
                    202:                }
1.1       misho     203:        }
                    204: 
                    205:        endpwent();
1.4       misho     206:        return ret;
1.1       misho     207: }
                    208: 
                    209: static int
                    210: LoadCfgData(char flg)
                    211: {
1.4       misho     212:        const char *str;
                    213:        char mode = 0;
1.1       misho     214: 
1.4       misho     215:        str = cfg_getAttribute(&cfg, "global", "mode");
1.1       misho     216:        if (!str) {
                    217:                Err("Error:: Unknown mode ...");
                    218:                return -1;
                    219:        }
1.6       misho     220:        if (!strcasecmp(str, "SCRIPT")) {
1.1       misho     221:                mode = 1;
1.6       misho     222:                if (setUIDGID(flg, (getenv("SUX_USER") ? NULL : getenv("PATH_TRANSLATED"))) == -1)
1.1       misho     223:                        return -1;
                    224:        } else if (!strcasecmp(str, "FILE")) {
                    225:                mode = 2;
1.4       misho     226:                if (setUIDGID(flg, AIT_GET_STR(&proc.proc_name)) == -1)
1.1       misho     227:                        return -1;
1.5       misho     228:        } else if (!strcasecmp(str, "DIR")) {
1.1       misho     229:                mode = 3;
1.5       misho     230:                str = AIT_GET_STR(&proc.proc_dir) ? AIT_GET_STR(&proc.proc_dir) : ".";
                    231:                if (setUIDGID(flg, str) == -1)
1.1       misho     232:                        return -1;
                    233:        } else {
                    234:                Err("Error:: Unknown mode %s", str);
                    235:                return -1;
                    236:        }
1.4       misho     237:        if (!(flg & SUX_GET_PRIO)) {
                    238:                str = cfg_getAttribute(&cfg, "global", "priority");
1.1       misho     239:                if (str)
1.4       misho     240:                        AIT_SET_I32(&proc.proc_prio, strtol(str, NULL, 10));
1.1       misho     241:        }
                    242: 
                    243:        /* find associate extension */
1.4       misho     244:        str = strrchr(AIT_GET_STR(&proc.proc_name), '.');
1.1       misho     245:        if (str) {
                    246:                str++;
                    247:                str = (*str) ? str : "default";
1.4       misho     248:                switch (cfg_loadAttribute(&cfg, "associate", str, &proc.proc_cmd, NULL)) {
1.1       misho     249:                        case -1:
1.4       misho     250:                                ELIBERR(cfg);
1.1       misho     251:                                return -1;
                    252:                        case 0:
1.5       misho     253:                                cfg_loadAttribute(&cfg, "associate", "default", 
                    254:                                                &proc.proc_cmd, DEFAULT_CMD);
1.1       misho     255:                }
                    256:        } else
1.4       misho     257:                AIT_SET_STR(&proc.proc_cmd, DEFAULT_CMD);
1.1       misho     258: 
                    259:        return 0;
                    260: }
                    261: 
                    262: static int
1.4       misho     263: Run(char **argv, char flg)
1.1       misho     264: {
                    265:        char **args, *cmd;
                    266:        array_t *acmd, *aarg;
                    267:        int n;
                    268: 
                    269:        if (!argv)
                    270:                return -1;
                    271: 
1.4       misho     272:        n = array_Args(AIT_GET_STR(&proc.proc_cmd), 0, " \t", &acmd);
1.1       misho     273:        if (n < 1)
                    274:                return -1;
1.4       misho     275:        if (!(aarg = array_From((const char***) &argv, 0))) {
                    276:                array_Destroy(&acmd);
1.1       misho     277:                return -1;
1.6       misho     278:        }
                    279:        /* '!' exclude associated wrapper aka direct args execution */
                    280:        if (*array(acmd, 0, char*) == '!') {
1.4       misho     281:                if (array_Grow(acmd, 0, 0)) {
                    282:                        array_Destroy(&aarg);
                    283:                        array_Destroy(&acmd);
1.1       misho     284:                        return -1;
                    285:                }
1.4       misho     286:                cmd = array(aarg, 0, char*);
1.1       misho     287:        } else
1.4       misho     288:                cmd = array(acmd, 0, char*);
1.1       misho     289: 
1.6       misho     290:        if (!(flg & SUX_GET_STDIN) && array_Concat(acmd, aarg) == -1) {
1.4       misho     291:                array_Destroy(&aarg);
                    292:                array_Destroy(&acmd);
1.1       misho     293:                return -1;
                    294:        }
1.4       misho     295:        array_Destroy(&aarg);
                    296:        if (!(args = array_To(acmd))) {
                    297:                array_Destroy(&acmd);
1.1       misho     298:                return -1;
                    299:        }
1.4       misho     300:        array_Destroy(&acmd);
1.1       misho     301: 
1.5       misho     302:        if (setClassDir()) {
1.1       misho     303:                if (args)
1.4       misho     304:                        e_free(args);
1.1       misho     305:                return -1;
                    306:        }
                    307: 
1.4       misho     308:        if (flg & SUX_GET_FORCE) {
                    309:                if (setegid(AIT_GET_I16(&proc.proc_gid)) == -1)
                    310:                        goto err;
                    311:                if (seteuid(AIT_GET_I16(&proc.proc_uid)) == -1)
                    312:                        goto err;
                    313:                if (setpriority(PRIO_PROCESS, 0, AIT_GET_I32(&proc.proc_prio)) == -1)
                    314:                        goto err;
1.1       misho     315:        }
1.2       misho     316: 
1.4       misho     317:        EVERBS(3) {
1.1       misho     318:                char **el = args - 1;
                    319:                while (*++el)
                    320:                        Log(3, "args: %s", *el);
                    321:        }
                    322: 
1.6       misho     323:        DumpProc(__func__);
                    324: 
1.2       misho     325:        fflush(lf);
                    326: 
1.1       misho     327:        execve(cmd, args, environ);
1.4       misho     328: err:
                    329:        ESYSERR(0);
1.1       misho     330:        if (args)
1.4       misho     331:                e_free(args);
1.1       misho     332:        return -1;
                    333: }
                    334: 
                    335: 
                    336: int
                    337: main(int argc, char **argv)
                    338: {
1.3       misho     339:        char ch, *str, *wrk, szCfg[MAXPATHLEN], **pp, flg = 0;
1.1       misho     340:        struct passwd *pass;
                    341:        struct group *grp;
1.2       misho     342:        FILE *f;
1.1       misho     343: 
1.2       misho     344:        initProg();
1.1       misho     345:        strlcpy(szCfg, DEFAULT_CONFIG, sizeof szCfg);
                    346: 
1.4       misho     347:        while ((ch = getopt(argc, argv, "hvoC:c:u:g:p:d:l:")) != -1)
1.1       misho     348:                switch (ch) {
1.2       misho     349:                        case 'l':
                    350:                                f = fopen(optarg, "a");
                    351:                                if (!f) {
                    352:                                        Err("Error:: logfile #%d - %s", errno, strerror(errno));
                    353:                                        return 1;
                    354:                                } else
                    355:                                        if (fileno(lf) > 2)
                    356:                                                fclose(lf);
                    357:                                lf = f;
                    358:                                break;
1.1       misho     359:                        case 'd':
1.4       misho     360:                                AIT_SET_STR(&proc.proc_dir, optarg);
                    361:                                flg |= SUX_GET_DIR;
1.1       misho     362:                                break;
                    363:                        case 'p':
1.4       misho     364:                                AIT_SET_I32(&proc.proc_prio, strtol(optarg, NULL, 0));
                    365:                                flg |= SUX_GET_PRIO;
1.1       misho     366:                                break;
                    367:                        case 'g':
                    368:                                setgrent();
                    369:                                grp = getgrnam(optarg);
                    370:                                if (grp) {
1.4       misho     371:                                        AIT_SET_I16(&proc.proc_gid, grp->gr_gid);
                    372:                                        flg |= SUX_GET_GID;
1.1       misho     373:                                } else
                    374:                                        Err("Error:: Group not found!");
                    375:                                endgrent();
                    376:                                break;
                    377:                        case 'u':
                    378:                                setpwent();
                    379:                                pass = getpwnam(optarg);
                    380:                                if (pass) {
1.4       misho     381:                                        AIT_SET_I16(&proc.proc_uid, pass->pw_uid);
                    382:                                        flg |= SUX_GET_UID;
1.1       misho     383:                                } else
                    384:                                        Err("Error:: User not found!");
                    385:                                endpwent();
                    386:                                break;
                    387:                        case 'c':
                    388:                                strlcpy(szCfg, optarg, sizeof szCfg);
                    389:                                break;
1.3       misho     390:                        case 'C':
                    391:                                if (chroot(optarg) == -1)
                    392:                                        Err("Error:: chroot to dir");
                    393:                                if ((str = getenv("PATH_TRANSLATED")))
                    394:                                        if ((wrk = strstr(str, optarg)))
                    395:                                                setenv("PATH_TRANSLATED", str + strlen(optarg), 42);
                    396:                                break;
1.4       misho     397:                        case 'o':
                    398:                                flg |= SUX_GET_FORCE;
                    399:                                break;
1.1       misho     400:                        case 'v':
1.4       misho     401:                                e_incVerbose;
1.1       misho     402:                                break;
                    403:                        case 'h':
                    404:                        default:
                    405:                                Usage();
1.4       misho     406:                                endProg();
1.2       misho     407:                                if (fileno(lf) > 2)
                    408:                                        fclose(lf);
1.1       misho     409:                                return 1;
                    410:                }
                    411:        argc -= optind;
                    412:        argv += optind;
1.2       misho     413: 
1.4       misho     414:        EVERBS(2) {
1.2       misho     415:                for (pp = argv; *pp; pp++)
                    416:                        Log(2, "Args=%s\n", *pp);
                    417:                for (pp = environ; *pp; pp++)
                    418:                        Log(2, "Envs=%s\n", *pp);
                    419:        }
                    420: 
1.1       misho     421:        if (!argc) {
                    422:                if (!(str = getenv("PATH_TRANSLATED"))) {
                    423:                        Usage();
1.4       misho     424:                        endProg();
1.2       misho     425:                        if (fileno(lf) > 2)
                    426:                                fclose(lf);
1.1       misho     427:                        return 1;
                    428:                } else
1.4       misho     429:                        AIT_SET_STR(&proc.proc_name, str);
1.6       misho     430:        } else if (strcmp(*argv, "-"))
1.4       misho     431:                AIT_SET_STR(&proc.proc_name, *argv);
1.6       misho     432:        else {
                    433:                flg |= SUX_GET_STDIN;
                    434:                AIT_SET_STR(&proc.proc_name, "-.stdin");        /* hack for associate to stdin */
                    435:        }
1.1       misho     436:        Log(2, "Try to load config %s", szCfg);
1.4       misho     437:        if (cfgLoadConfig(szCfg, &cfg)) {
                    438:                ELIBERR(cfg);
                    439:                endProg();
1.2       misho     440:                if (fileno(lf) > 2)
                    441:                        fclose(lf);
1.1       misho     442:                return 2;
                    443:        } else
                    444:                if (LoadCfgData(flg) == -1) {
1.4       misho     445:                        cfgUnloadConfig(&cfg);
                    446:                        endProg();
1.2       misho     447:                        if (fileno(lf) > 2)
                    448:                                fclose(lf);
1.1       misho     449:                        closelog();
                    450:                        return 3;
                    451:                }
1.4       misho     452:        cfgUnloadConfig(&cfg);
1.1       misho     453: 
1.4       misho     454:        if (Run(argv, flg) == -1) {
                    455:                endProg();
1.2       misho     456:                if (fileno(lf) > 2)
                    457:                        fclose(lf);
1.1       misho     458:                closelog();
                    459:                return 4;
                    460:        }
                    461: 
1.4       misho     462:        endProg();
1.2       misho     463:        if (fileno(lf) > 2)
                    464:                fclose(lf);
1.1       misho     465:        closelog();
                    466:        return 0;
                    467: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>