From 3cf31a194f5721b67b1255e3f168d4bc87d433fc Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 15 Feb 2000 19:36:47 +0000 Subject: Added replacement functions sys_popen and sys_pclose. These are based on the glibc source code and are safer than the traditional popen as they don't use a shell to exec the requested command. Now we have these functions they can be tightened up (environment etc.) as required to make a safe popen. It should now be safe to add the environement variable loading code to loadparm.c Jeremy. (This used to be commit b52e92b09d4ca3b66e534f520468dee27065d048) --- source3/lib/debug.c | 12 ++- source3/lib/replace.c | 16 +++- source3/lib/system.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 244 insertions(+), 15 deletions(-) (limited to 'source3/lib') diff --git a/source3/lib/debug.c b/source3/lib/debug.c index a0dfe61f7d..c88f4e1a41 100644 --- a/source3/lib/debug.c +++ b/source3/lib/debug.c @@ -318,7 +318,8 @@ va_dcl va_start( ap ); format_str = va_arg( ap, char * ); #endif - (void)vfprintf( dbf, format_str, ap ); + if(dbf) + (void)vfprintf( dbf, format_str, ap ); va_end( ap ); errno = old_errno; return( 0 ); @@ -397,9 +398,11 @@ va_dcl va_start( ap ); format_str = va_arg( ap, char * ); #endif - (void)vfprintf( dbf, format_str, ap ); + if(dbf) + (void)vfprintf( dbf, format_str, ap ); va_end( ap ); - (void)fflush( dbf ); + if(dbf) + (void)fflush( dbf ); } errno = old_errno; @@ -488,7 +491,8 @@ static void format_debug_text( char *msg ) void dbgflush( void ) { bufr_print(); - (void)fflush( dbf ); + if(dbf) + (void)fflush( dbf ); } /* dbgflush */ /* ************************************************************************** ** diff --git a/source3/lib/replace.c b/source3/lib/replace.c index 6a492f977c..8d91c2d785 100644 --- a/source3/lib/replace.c +++ b/source3/lib/replace.c @@ -163,15 +163,21 @@ Corrections by richard.kettlewell@kewill.com /* yikes! no SETGROUPS or INITGROUPS? how can this work? */ return(0); #else /* HAVE_SETGROUPS */ - gid_t grouplst[NGROUPS_MAX]; + gid_t *grouplst = NULL; + int max_gr = groups_max(); + int ret; int i,j; struct group *g; char *gr; + if((grouplst = (gid_t *)malloc(sizeof(gid_t) * max_gr)) == NULL) { + DEBUG(0,("initgroups: malloc fail !\n"); + return -1; + } + grouplst[0] = id; i = 1; - while (i < NGROUPS_MAX && - ((g = (struct group *)getgrent()) != (struct group *)NULL)) { + while (i < max_gr && ((g = (struct group *)getgrent()) != (struct group *)NULL)) { if (g->gr_gid == id) continue; j = 0; @@ -187,7 +193,9 @@ Corrections by richard.kettlewell@kewill.com } } endgrent(); - return(sys_setgroups(i,grouplst)); + ret = sys_setgroups(i,grouplst); + free((char *)grouplst); + return ret; #endif /* HAVE_SETGROUPS */ } #endif /* HAVE_INITGROUPS */ diff --git a/source3/lib/system.c b/source3/lib/system.c index d146749974..25925b6d8e 100644 --- a/source3/lib/system.c +++ b/source3/lib/system.c @@ -561,6 +561,20 @@ void sys_srandom(unsigned int seed) #endif } +/************************************************************************** + Returns equivalent to NGROUPS_MAX - using sysconf if needed. +****************************************************************************/ + +int groups_max(void) +{ +#if defined(SYSCONF_SC_NGROUPS_MAX) + int ret = sysconf(_SC_NGROUPS_MAX); + return (ret == -1) ? NGROUPS_MAX : ret; +#else + return NGROUPS_MAX; +#endif +} + /************************************************************************** Wrapper for getgroups. Deals with broken (int) case. ****************************************************************************/ @@ -590,7 +604,7 @@ int sys_getgroups(int setlen, gid_t *gidset) } if (setlen == 0) - setlen = 1; + setlen = groups_max(); if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) { DEBUG(0,("sys_getgroups: Malloc fail.\n")); @@ -631,21 +645,16 @@ int sys_setgroups(int setlen, gid_t *gidset) if (setlen == 0) return 0 ; -#ifdef NGROUPS_MAX - if (setlen > NGROUPS_MAX) { + if (setlen < 0 || setlen > groups_max()) { errno = EINVAL; return -1; } -#endif /* * Broken case. We need to allocate a * GID_T array of size setlen. */ - if (setlen == 0) - setlen = 1; - if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) { DEBUG(0,("sys_setgroups: Malloc fail.\n")); return -1; @@ -892,3 +901,211 @@ SMB_STRUCT_WPASSWD *wsys_getpwuid(uid_t uid) return &retval; } + +/************************************************************************** + Extract a command into an arg list. Uses a static pstring for storage. + Caller frees returned arg list (which contains pointers into the static pstring). +****************************************************************************/ + +static char **extract_args(const char *command) +{ + static pstring trunc_cmd; + char *ptr; + int argcl; + char **argl = NULL; + int i; + + pstrcpy(trunc_cmd, command); + + if(!(ptr = strtok(trunc_cmd, " \t"))) { + errno = EINVAL; + return NULL; + } + + /* + * Count the args. + */ + + for( argcl = 1; ptr; ptr = strtok(NULL, " \t")) + argcl++; + + if((argl = (char **)malloc((argcl + 1) * sizeof(char *))) == NULL) + return NULL; + + /* + * Now do the extraction. + */ + + pstrcpy(trunc_cmd, command); + + ptr = strtok(trunc_cmd, " \t"); + i = 0; + argl[i++] = ptr; + + while((ptr = strtok(NULL, " \t")) != NULL) + argl[i++] = ptr; + + argl[i++] = NULL; + return argl; +} + +/************************************************************************** + Wrapper for popen. Safer as it doesn't search a path. + Modified from the glibc sources. +****************************************************************************/ + +typedef struct _popen_list +{ + FILE *fp; + pid_t child_pid; + struct _popen_list *next; +} popen_list; + +static popen_list *popen_chain; + +FILE *sys_popen(const char *command, const char *mode) +{ + int parent_end, child_end; + int pipe_fds[2]; + popen_list *entry = NULL; + pid_t child_pid; + char **argl = NULL; + + if (pipe(pipe_fds) < 0) + return NULL; + + if (mode[0] == 'r' && mode[1] == '\0') { + parent_end = pipe_fds[0]; + child_end = pipe_fds[1]; + } else if (mode[0] == 'w' && mode[1] == '\0') { + parent_end = pipe_fds[1]; + child_end = pipe_fds[0]; + } else { + errno = EINVAL; + goto err_exit; + } + + if (!*command) { + errno = EINVAL; + goto err_exit; + } + + if((entry = (popen_list *)malloc(sizeof(popen_list))) == NULL) + goto err_exit; + + /* + * Extract the command and args into a NULL terminated array. + */ + + if(!(argl = extract_args(command))) + goto err_exit; + + entry->child_pid = fork(); + + if (entry->child_pid == -1) { + + /* + * Error ! + */ + + goto err_exit; + } + + if (entry->child_pid == 0) { + + /* + * Child ! + */ + + int child_std_end = (mode[0] == 'r') ? STDOUT_FILENO : STDIN_FILENO; + popen_list *p; + + close(parent_end); + if (child_end != child_std_end) { + dup2 (child_end, child_std_end); + close (child_end); + } + + /* + * POSIX.2: "popen() shall ensure that any streams from previous + * popen() calls that remain open in the parent process are closed + * in the new child process." + */ + + for (p = popen_chain; p; p = p->next) + close(fileno(p->fp)); + + execv(argl[0], argl); + _exit (127); + } + + /* + * Parent. + */ + + close (child_end); + free((char *)argl); + + /* + * Create the FILE * representing this fd. + */ + entry->fp = fdopen(parent_end, mode); + + /* Link into popen_chain. */ + entry->next = popen_chain; + popen_chain = entry; + + return entry->fp; + +err_exit: + + if(entry) + free((char *)entry); + if(argl) + free((char *)argl); + close(pipe_fds[0]); + close(pipe_fds[1]); + return NULL; +} + +/************************************************************************** + Wrapper for pclose. Modified from the glibc sources. +****************************************************************************/ + +int sys_pclose( FILE *fp) +{ + int wstatus; + popen_list **ptr = &popen_chain; + popen_list *entry = NULL; + pid_t wait_pid; + int status = -1; + + /* Unlink from popen_chain. */ + for ( ; *ptr != NULL; ptr = &(*ptr)->next) { + if ((*ptr)->fp == fp) { + entry = *ptr; + *ptr = (*ptr)->next; + status = 0; + break; + } + } + + if (status < 0 || close(fileno(entry->fp)) < 0) + return -1; + + /* + * As Samba is catching and eating child process + * exits we don't really care about the child exit + * code, a -1 with errno = ECHILD will do fine for us. + */ + + do { + wait_pid = sys_waitpid (entry->child_pid, &wstatus, 0); + } while (wait_pid == -1 && errno == EINTR); + + free((char *)entry); + + if (wait_pid == -1) + return -1; + return wstatus; +} -- cgit