/* OSSP rc - Run-command processor ** Copyright (c) 2002 Ralf S. Engelschall ** Copyright (c) 2002 Cable & Wireless Deutschland GmbH ** Copyright (c) 2002 The OSSP Project ** ** This file is part of OSSP rc, a portable Run-command processor ** which can be found at http://www.ossp.org/pkg/lib/rc/ ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** rc_proc.c: Run-command processor ISO C source file */ #include /* Standard system headers */ #include /* For reading rc files */ #include /* For reading rc files */ #include /* For string manipulation */ #include /* For signal(3) */ #include /* For waitpid(2) and fork(2) */ #include "rc.h" /* Public interfaces */ #include "rc_const.h" /* String and value const */ #include "rc_config.h" /* Option definitions */ /************************************************ * procNew(void) * * Construct a processor * ************************************************/ rc_proc_t *procNew(void) { rc_proc_t *pNewrc = NULL; pNewrc = malloc(sizeof(rc_proc_t)); pNewrc->m_pAnal = analNew(); /* Construct a configuration analyser */ analParse(pNewrc->m_pAnal); /* Preprocess the anal configuration */ pNewrc->m_pScriptcom = scriptNew(); /* Construct a run-command script */ pNewrc->m_ppLabvec = calloc(pNewrc->m_pAnal->m_nRcs, sizeof(rc_label_t *)); return(pNewrc); } /************************************************ * procReadtmp(rc_proc_t *, const char *) * * Open and store a temp file * ************************************************/ rc_return_t procReadtmp(rc_proc_t *pRc, const char *szTmpname) { fprintf(stderr, "%s!!!\n", szTmpname); return(RC_THROW(RC_OK)); } /************************************************ * procPopulate(rc_proc_t *) * * Populate the processor with run commands * ************************************************/ rc_return_t procPopulate(rc_proc_t *pRc) { int nSect = 0; int nFdrc = -1; int nFdfunc = -1; int nRet = 0; int nRc = 0; ex_t Except; char *sBuf = NULL; rc_section_t *pSec = NULL; char *szLocex = NULL; rc_script_t *pTempscript = NULL; assert(*pRc->m_pAnal->m_szRcs); sBuf = (char *)calloc(1, RC_READ_BUFSIZE); /* Stick on the starting shell id line */ scriptAppend(pRc->m_pScriptcom, "#! /bin/sh\n", strlen("#! /bin/sh\n")); /* Open the func file if it exists in the configuration */ if (pRc->m_pAnal->m_szFuncs) { if ((nFdfunc = open(pRc->m_pAnal->m_szFuncs, O_RDONLY)) >= 0) { /* Read data from the func file */ while ((nRet = read(nFdfunc, sBuf, RC_READ_BUFSIZE)) > 0) { scriptAppend(pRc->m_pScriptcom, sBuf, nRet); } if (nRet == -1) /* Handle read errors */ RC_THROW(RC_ERR_IO); scriptAppend(pRc->m_pScriptcom, "\n", sizeof(char)); } else RC_THROW(RC_ERR_FNC); } /* Iteratively read possibly globbed rc files */ for (nRc = 0; nRc < pRc->m_pAnal->m_nRcs; nRc++) { assert(*pRc->m_pAnal->m_szRcs); /* If one of these assertions fail, */ assert(pRc->m_pAnal->m_szLocs); /* you've probably seen the ex_ bug */ /* Construct a new label */ pRc->m_ppLabvec[nRc] = labelNew(pRc->m_pAnal->m_szRcs[nRc]); /* Build the path name */ szLocex = (char *)malloc(strlen(pRc->m_pAnal->m_szLocs) + strlen(pRc->m_pAnal->m_szRcs[nRc]) + 2); strcpy(szLocex, pRc->m_pAnal->m_szLocs); strcat(szLocex, "rc."); /* FIXME: Make the prefix configurable */ strcat(szLocex, pRc->m_pAnal->m_szRcs[nRc]); /* Open the rc file unconditionally */ if ((nFdrc = open(szLocex, O_RDONLY)) == -1) RC_THROW(RC_ERR_RCF); /* Read data from the rc file into a temporary script */ pTempscript = scriptNew(); while ((nRet = read(nFdrc, sBuf, RC_READ_BUFSIZE)) > 0) scriptAppend(pTempscript, sBuf, nRet); if (nRet == -1) /* Handle read errors */ RC_THROW(RC_ERR_IO); try { /* Append config section if it exists */ pSec = scriptSection(pTempscript, configGetval(RC_NCF_VAL)); if (pSec) { /* Only operate if the section lookup succeeds */ scriptAppend(pRc->m_pScriptcom, sectionGetdata(pSec), strlen(sectionGetdata(pSec))); scriptAppend(pRc->m_pScriptcom, "\n", sizeof(char)); sectionDelete(pSec); /* Cleanup */ pSec = NULL; /* Cleanup */ } for (nSect = 0; nSect < pRc->m_pAnal->m_nSecs; nSect++) { /* Iterate over */ /* Extract a section from the temp script, and append it */ pSec = scriptSection(pTempscript, pRc->m_pAnal->m_pszSecs[nSect]); if (pSec) /* Only copy if the section lookup succeeds */ labelAppendsec(pRc->m_ppLabvec[nRc], pSec); else if (configGetval(RC_DBG_VAL)) /* Only show if debug set */ fprintf(stderr, "#Warning: Missing section '%s' in %s!\n",\ pRc->m_pAnal->m_pszSecs[nSect],\ pRc->m_pAnal->m_szRcs[nRc]); if (pSec) { /* Cleanup iterative section string */ sectionDelete(pSec); pSec = NULL; } } } catch(Except) rethrow; /* Clean up our crap */ scriptDelete(pTempscript); /* Temp script */ pTempscript = NULL; free(szLocex); /* Temp Location + Rcfile */ szLocex = NULL; close(nFdrc); /* Close Rc file handle */ } close(nFdfunc); /* Close Func file handle */ /* Memory cleanups */ if (sBuf) { free(sBuf); sBuf = NULL; } return(RC_THROW(RC_OK)); } /************************************************ * procRun(rc_proc_t *) * * Run the processed run-command script * ************************************************/ rc_return_t procRun(rc_proc_t *pRc) { int nTmp = 0; /* Generic index */ int nTmpname = 0; /* Temp file name size */ int nRcs = 0; /* Rc index */ int nSecs = 0; /* Section index */ int nSectuid = -1; /* The section's user id */ int nRunuid = -1; /* The current user id */ pid_t Pidexec = -1; /* When spawning before execv(3) */ char *szTmpfile = NULL; /* Path of temporary file */ char *szTmp = NULL; /* Generic temporary string */ char *szCom = NULL; /* Stores common script text */ char *szExec = NULL; /* Used only during exec mode */ char *pszVec[RC_EXEC_MAXARGS]; /* For passing in to execv(3) */ rc_script_t *pFatscript = NULL; /* To build a comprehensive script */ rc_section_t **ppSectmp = NULL; /* Used with priority scheduling */ /****************************************************/ /* This will execute, evaluate, or print the script */ /* Exec - Fork and execute each command */ /* Eval - Print machine evaluatable format */ /* Print - Print human readable format */ /****************************************************/ if (configGetval(RC_EVL_VAL)) { /* Evaluate */ /* Allocate a block of section pointers to use temporarily */ ppSectmp = calloc(pRc->m_pAnal->m_nRcs, sizeof(rc_section_t *)); /* Allocate the command chain string to execute with execv(3) */ pFatscript = scriptCopy(pRc->m_pScriptcom); for (nSecs = 0; nSecs < pRc->m_pAnal->m_nSecs; nSecs++) { for (nRcs = 0; nRcs < pRc->m_pAnal->m_nRcs; nRcs++) { nTmp = 0; while (nTmp < pRc->m_ppLabvec[nRcs]->m_nSecs && \ strcmp(pRc->m_ppLabvec[nRcs]->m_ppSecvec[nTmp]->m_szName, \ pRc->m_pAnal->m_pszSecs[nSecs])) nTmp++; if (nTmp < pRc->m_ppLabvec[nRcs]->m_nSecs) ppSectmp[nRcs] = pRc->m_ppLabvec[nRcs]->m_ppSecvec[nTmp]; else ppSectmp[nRcs] = NULL; } qsort((void *)ppSectmp, (size_t)pRc->m_pAnal->m_nRcs, \ sizeof(rc_section_t *), priCompare); nTmp = 0; while (nTmp < pRc->m_pAnal->m_nRcs && ppSectmp[nTmp]) { if ((szTmp = (char *)sectionGetlogin(ppSectmp[nTmp])) != NULL) { scriptAppend(pFatscript, "#su ", strlen("#su ")); scriptAppend(pFatscript, szTmp, strlen(szTmp)); scriptAppend(pFatscript, "\n", strlen("\n") + 1); } szTmp = (char *)sectionGetdata(ppSectmp[nTmp]); scriptAppend(pFatscript, szTmp, strlen(szTmp) + 1); nTmp++; } } free(ppSectmp); ppSectmp = NULL; szTmpfile = (char *)configGetval(RC_TMP_VAL); nTmpname = (strlen(szTmpfile) + strlen(RC_EVL_TMP) + \ strlen(RC_EVL_SUF) + 1) * sizeof(char); if (*(szTmpfile + (strlen(szTmpfile) - 1) * sizeof(char)) != '/') nTmpname += sizeof(char); szTmpfile = malloc(nTmpname); strcpy(szTmpfile, configGetval(RC_TMP_VAL)); if (*(szTmpfile + (strlen(szTmpfile) - 1) * sizeof(char)) != '/') *(szTmpfile + (strlen(szTmpfile)) * sizeof(char)) = '/'; strcat(szTmpfile, RC_EVL_TMP); mktemp(szTmpfile); strcat(szTmpfile, RC_EVL_SUF); scriptWrite(pFatscript, szTmpfile); /* Write the whole script out */ fprintf(stdout, RC_EVL_OUT, szTmpfile, szTmpfile); free(szTmpfile); szTmpfile = NULL; scriptDelete(pFatscript); pFatscript = NULL; } else if (configGetval(RC_EXC_VAL)) { /* Execute */ /* This block does nothing more than implement the feature, */ /* that allows rc to run unprivileged (as long as no privileged */ /* code is used in the script sections to be executed */ for (nSecs = 0; nSecs < pRc->m_pAnal->m_nSecs; nSecs++) { for (nTmp = 0; nTmp < pRc->m_pAnal->m_nRcs; nTmp++) { if (pRc->m_ppLabvec[nTmp]->m_ppSecvec) { nRunuid = getuid(); nSectuid = pRc->m_ppLabvec[nTmp]->m_ppSecvec[nSecs]->m_nUid; /* See if root user status is needed, and bail out if so */ if (nRunuid != 0 && nSectuid != -1 && nRunuid != nSectuid) { fprintf(stderr, RC_RUT_TEXT); return(RC_THROW(RC_ERR_USE)); } } } } /* Allocate a block of section pointers to use temporarily */ ppSectmp = calloc(pRc->m_pAnal->m_nRcs, sizeof(rc_section_t *)); szCom = (char *)scriptTostring(pRc->m_pScriptcom); for (nSecs = 0; nSecs < pRc->m_pAnal->m_nSecs; nSecs++) { for (nRcs = 0; nRcs < pRc->m_pAnal->m_nRcs; nRcs++) { nTmp = 0; while (nTmp < pRc->m_ppLabvec[nRcs]->m_nSecs && \ strcmp(pRc->m_ppLabvec[nRcs]->m_ppSecvec[nTmp]->m_szName, \ pRc->m_pAnal->m_pszSecs[nSecs])) nTmp++; if (nTmp < pRc->m_ppLabvec[nRcs]->m_nSecs) ppSectmp[nRcs] = pRc->m_ppLabvec[nRcs]->m_ppSecvec[nTmp]; else ppSectmp[nRcs] = NULL; } qsort((void *)ppSectmp, (size_t)pRc->m_pAnal->m_nRcs, sizeof(rc_section_t *), priCompare); pszVec[0] = "/bin/sh"; /* Run the bourne shell over the following */ pszVec[1] = "-c"; /* Append script code of the sections */ pszVec[3] = NULL; /* Add a NULL to mark the end of the chain */ nTmp = 0; /* Count from zero until however many sections we have */ while (nTmp < pRc->m_pAnal->m_nRcs && ppSectmp[nTmp]) { szTmp = (char *)sectionGetdata(ppSectmp[nTmp]); szExec = malloc(strlen(szCom) * sizeof(char) + \ (strlen(szTmp) + 1) * sizeof(char)); strcpy(szExec, szCom); /* Start out with just the common script code */ strcat(szExec, szTmp); /* And build a section onto the command chain */ pszVec[2] = szExec; /* Actually launch the new process image now */ /* Spawn the section shell code */ switch (Pidexec = fork()){ case -1: /* Broken */ return(RC_THROW(RC_ERR_INT)); break; /* Huh? */ case 0: /* Child, runs script code through bourne shell */ nSectuid = sectionGetuid(ppSectmp[nTmp]); if (nSectuid >= 0 && getuid() != nSectuid) if (setuid(nSectuid) != 0) return(RC_THROW(RC_ERR_INT)); if (execvp(*pszVec, pszVec) == -1) return(RC_THROW(RC_ERR_INT)); break; default: /* Parent, blocks until child returns */ waitpid(Pidexec, NULL, WUNTRACED); break; } free(szExec); /* Cleanup after exec */ szExec = NULL; nTmp++; } } free(ppSectmp); ppSectmp = NULL; } else if (configGetval(RC_PRN_VAL)) { /* Print */ /* Allocate a block of section pointers to use as a temporary */ ppSectmp = calloc(pRc->m_pAnal->m_nRcs, sizeof(rc_section_t *)); scriptDump(pRc->m_pScriptcom); /* Dump the common script */ for (nSecs = 0; nSecs < pRc->m_pAnal->m_nSecs; nSecs++) { for (nRcs = 0; nRcs < pRc->m_pAnal->m_nRcs; nRcs++) { nTmp = 0; while (nTmp < pRc->m_ppLabvec[nRcs]->m_nSecs && \ strcmp(pRc->m_ppLabvec[nRcs]->m_ppSecvec[nTmp]->m_szName, \ pRc->m_pAnal->m_pszSecs[nSecs])) nTmp++; if (nTmp < pRc->m_ppLabvec[nRcs]->m_nSecs) ppSectmp[nRcs] = pRc->m_ppLabvec[nRcs]->m_ppSecvec[nTmp]; else ppSectmp[nRcs] = NULL; } qsort((void *)ppSectmp, (size_t)pRc->m_pAnal->m_nRcs, sizeof(rc_section_t *), priCompare); nTmp = 0; while (nTmp < pRc->m_pAnal->m_nRcs && ppSectmp[nTmp]) { sectionDump(ppSectmp[nTmp]); nTmp++; } } free(ppSectmp); ppSectmp = NULL; } else /* Something is wrong here */ return(RC_THROW(RC_ERR_INT)); return(RC_THROW(RC_OK)); } /************************************************ * procDelete(rc_proc_t *) * * Destruct a processor * ************************************************/ rc_return_t procDelete(rc_proc_t *pRc) { int nRcs = pRc->m_pAnal->m_nRcs; /* Destroy the label vector */ while (nRcs-- > 0) { if (pRc->m_ppLabvec[nRcs]) { labelDelete(pRc->m_ppLabvec[nRcs]); pRc->m_ppLabvec[nRcs] = NULL; } } free(pRc->m_ppLabvec); pRc->m_ppLabvec = NULL; scriptDelete(pRc->m_pScriptcom); /* Destroy the script */ analDelete(pRc->m_pAnal); /* Destroy the analyser */ free(pRc); /* Free the processor itself */ return(RC_THROW(RC_OK)); }