1/* 2 * Copyright (c) 1988 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software written by Ken Arnold and 6 * published in UNIX Review, Vol. 6, No. 8. 7 * 8 * Redistribution and use in source and binary forms are permitted 9 * provided that the above copyright notice and this paragraph are 10 * duplicated in all such forms and that any documentation, 11 * advertising materials, and other materials related to such 12 * distribution and use acknowledge that the software was developed 13 * by the University of California, Berkeley. The name of the 14 * University may not be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19 * 20 */ 21 22/* this came out of the ftpd sources; it's been modified to avoid the 23 * globbing stuff since we don't need it. also execvp instead of execv. 24 */ 25 26#ifndef lint 27#if 0 28static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89"; 29#endif 30static const char rcsid[] = 31 "$FreeBSD: src/usr.sbin/cron/cron/popen.c,v 1.15 2006/06/11 21:13:49 maxim Exp $"; 32#endif /* not lint */ 33 34#include "cron.h" 35#include <sys/signal.h> 36#include <fcntl.h> 37#include <paths.h> 38#if defined(SYSLOG) 39# include <syslog.h> 40#endif 41#if defined(LOGIN_CAP) 42# include <login_cap.h> 43#endif 44 45 46#define MAX_ARGS 100 47#define WANT_GLOBBING 0 48 49/* 50 * Special version of popen which avoids call to shell. This insures noone 51 * may create a pipe to a hidden program as a side effect of a list or dir 52 * command. 53 */ 54static PID_T *pids; 55static int fds; 56 57FILE * 58cron_popen(program, type, e) 59 char *program, *type; 60 entry *e; 61{ 62 register char *cp; 63 FILE *iop; 64 int argc, pdes[2]; 65 PID_T pid; 66#ifndef __APPLE__ 67 char *usernm; 68#endif 69 char *argv[MAX_ARGS + 1]; 70# if defined(LOGIN_CAP) 71 struct passwd *pwd; 72 login_cap_t *lc; 73# endif 74#if WANT_GLOBBING 75 char **pop, *vv[2]; 76 int gargc; 77 char *gargv[1000]; 78 extern char **glob(), **copyblk(); 79#endif 80 81 if ((*type != 'r' && *type != 'w') || type[1]) 82 return(NULL); 83 84 if (!pids) { 85 if ((fds = getdtablesize()) <= 0) 86 return(NULL); 87 if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T))))) 88 return(NULL); 89 bzero((char *)pids, fds * sizeof(PID_T)); 90 } 91 if (pipe(pdes) < 0) 92 return(NULL); 93 94 /* break up string into pieces */ 95 for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL) 96 if (!(argv[argc++] = strtok(cp, " \t\n"))) 97 break; 98 argv[MAX_ARGS] = NULL; 99 100#if WANT_GLOBBING 101 /* glob each piece */ 102 gargv[0] = argv[0]; 103 for (gargc = argc = 1; argv[argc]; argc++) { 104 if (!(pop = glob(argv[argc]))) { /* globbing failed */ 105 vv[0] = argv[argc]; 106 vv[1] = NULL; 107 pop = copyblk(vv); 108 } 109 argv[argc] = (char *)pop; /* save to free later */ 110 while (*pop && gargc < 1000) 111 gargv[gargc++] = *pop++; 112 } 113 gargv[gargc] = NULL; 114#endif 115 116 iop = NULL; 117 switch(pid = vfork()) { 118 case -1: /* error */ 119 (void)close(pdes[0]); 120 (void)close(pdes[1]); 121 goto pfree; 122 /* NOTREACHED */ 123 case 0: /* child */ 124 if (e != NULL) { 125#ifdef SYSLOG 126 closelog(); 127#endif 128 129 /* get new pgrp, void tty, etc. 130 */ 131 (void) setsid(); 132 } 133 if (*type == 'r') { 134 /* Do not share our parent's stdin */ 135 (void)close(0); 136 (void)open(_PATH_DEVNULL, O_RDWR); 137 if (pdes[1] != 1) { 138 dup2(pdes[1], 1); 139 dup2(pdes[1], 2); /* stderr, too! */ 140 (void)close(pdes[1]); 141 } 142 (void)close(pdes[0]); 143 } else { 144 if (pdes[0] != 0) { 145 dup2(pdes[0], 0); 146 (void)close(pdes[0]); 147 } 148 /* Hack: stdout gets revoked */ 149 (void)close(1); 150 (void)open(_PATH_DEVNULL, O_RDWR); 151 (void)close(2); 152 (void)open(_PATH_DEVNULL, O_RDWR); 153 (void)close(pdes[1]); 154 } 155 if (e != NULL) { 156 /* Set user's entire context, but skip the environment 157 * as cron provides a separate interface for this 158 */ 159#ifdef __APPLE__ 160 struct passwd *pwd; 161 uid_t uid; 162 gid_t gid; 163 164 if ((pwd = getpwnam(e->uname))) { 165 char envstr[MAXPATHLEN + sizeof "HOME="]; 166 167 uid = pwd->pw_uid; 168 gid = pwd->pw_gid; 169 170 if (pwd->pw_expire && time(NULL) >= pwd->pw_expire) { 171 warn("user account expired: %s", e->uname); 172 _exit(ERROR_EXIT); 173 } 174 175 sprintf(envstr, "HOME=%s", pwd->pw_dir); 176 e->envp = env_set(e->envp, envstr); 177 if (e->envp == NULL) { 178 warn("env_set(%s)", envstr); 179 _exit(ERROR_EXIT); 180 } 181 } else { 182 warn("getpwnam(\"%s\")", e->uname); 183 _exit(ERROR_EXIT); 184 } 185 186 if (strlen(e->gname) > 0) { 187 struct group *gr = getgrnam(e->gname); 188 if (gr) { 189 gid = gr->gr_gid; 190 } else { 191 warn("getgrnam(\"%s\")", e->gname); 192 _exit(ERROR_EXIT); 193 } 194 } 195#else /* __APPLE__ */ 196 usernm = env_get("LOGNAME", e->envp); 197#endif /* __APPLE__ */ 198# if defined(LOGIN_CAP) 199 if ((pwd = getpwnam(usernm)) == NULL) 200 pwd = getpwuid(e->uid); 201 lc = NULL; 202 if (pwd != NULL) { 203 pwd->pw_gid = e->gid; 204 if (e->class != NULL) 205 lc = login_getclass(e->class); 206 } 207 if (pwd && 208 setusercontext(lc, pwd, e->uid, 209 LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0) 210 (void) endpwent(); 211 else { 212 /* fall back to the old method */ 213 (void) endpwent(); 214# endif 215 /* 216 * Set our directory, uid and gid. Set gid 217 * first since once we set uid, we've lost 218 * root privileges. 219 */ 220#ifdef __APPLE__ 221 if(setgid(gid)) { 222 warn("setgid failed"); 223 _exit(ERROR_EXIT); 224 } 225# if defined(BSD) 226 if (initgroups(e->uname, gid) != 0) { 227 warn("initgroups failed"); 228 _exit(ERROR_EXIT); 229 } 230# endif 231 if (setlogin(e->uname) != 0) { 232 warn("setlogin failed"); 233 _exit(ERROR_EXIT); 234 } 235 if(setuid(uid)) { /* we aren't root after this..*/ 236 warn("setuid failed"); 237 _exit(ERROR_EXIT); 238 } 239#else /* __APPLE__ */ 240 if (setgid(e->gid) != 0) 241 _exit(ERROR_EXIT); 242# if defined(BSD) 243 if (initgroups(usernm, e->gid) != 0) 244 _exit(ERROR_EXIT); 245# endif 246 if (setlogin(usernm) != 0) 247 _exit(ERROR_EXIT); 248 if (setuid(e->uid) != 0) 249 _exit(ERROR_EXIT); 250 /* we aren't root after this..*/ 251#endif /* __APPLE__ */ 252#if defined(LOGIN_CAP) 253 } 254 if (lc != NULL) 255 login_close(lc); 256#endif 257 chdir(env_get("HOME", e->envp)); 258 } 259#if WANT_GLOBBING 260 execvp(gargv[0], gargv); 261#else 262 execvp(argv[0], argv); 263#endif 264 _exit(1); 265 } 266 /* parent; assume fdopen can't fail... */ 267 if (*type == 'r') { 268 iop = fdopen(pdes[0], type); 269 (void)close(pdes[1]); 270 } else { 271 iop = fdopen(pdes[1], type); 272 (void)close(pdes[0]); 273 } 274 pids[fileno(iop)] = pid; 275 276pfree: 277#if WANT_GLOBBING 278 for (argc = 1; argv[argc] != NULL; argc++) { 279/* blkfree((char **)argv[argc]); */ 280 free((char *)argv[argc]); 281 } 282#endif 283 return(iop); 284} 285 286int 287cron_pclose(iop) 288 FILE *iop; 289{ 290 register int fdes; 291 int omask; 292 WAIT_T stat_loc; 293 PID_T pid; 294 295 /* 296 * pclose returns -1 if stream is not associated with a 297 * `popened' command, or, if already `pclosed'. 298 */ 299 if (pids == 0 || pids[fdes = fileno(iop)] == 0) 300 return(-1); 301 (void)fclose(iop); 302 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 303 while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) 304 ; 305 (void)sigsetmask(omask); 306 pids[fdes] = 0; 307 return (pid == -1 ? -1 : WEXITSTATUS(stat_loc)); 308} 309