/* 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_pszRcs);
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_pszRcs);
assert(pRc->m_pAnal->m_szLocs);
/* Construct a new label */
pRc->m_ppLabvec[nRc] = labelNew(pRc->m_pAnal->m_pszRcs[nRc]);
/* Build the path name */
szLocex = (char *)malloc(strlen(pRc->m_pAnal->m_szLocs) + \
strlen(pRc->m_pAnal->m_pszRcs[nRc]) + \
strlen("rc.") + 1);
strcpy(szLocex, pRc->m_pAnal->m_szLocs);
strcat(szLocex, "rc."); /* FIXME: Make the prefix configurable */
strcat(szLocex, pRc->m_pAnal->m_pszRcs[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_pszRcs[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)) != '/')
strcat(szTmpfile, "/");
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 && \
pRc->m_ppLabvec[nTmp]->m_ppSecvec[nSecs]) {
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));
}