1 /*
2  * Sandbox helper for CUPS.
3  *
4  * Copyright 2007-2014 by Apple Inc.
5  *
6  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
7  *
8  * Usage:
9  *
10  *     cups-exec /path/to/profile [-u UID] [-g GID] [-n NICE] /path/to/program argv0 argv1 ... argvN
11  */
12 
13 /*
14  * Include necessary headers...
15  */
16 
17 #include <cups/string-private.h>
18 #include <cups/file.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <grp.h>
22 #include <sys/stat.h>
23 #ifdef HAVE_SANDBOX_H
24 #  include <sandbox.h>
25 #  ifndef SANDBOX_NAMED_EXTERNAL
26 #    define SANDBOX_NAMED_EXTERNAL  0x0003
27 #  endif /* !SANDBOX_NAMED_EXTERNAL */
28 #  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
29 #endif /* HAVE_SANDBOX_H */
30 
31 
32 /*
33  * Local functions...
34  */
35 
36 static void	usage(void) _CUPS_NORETURN;
37 
38 
39 /*
40  * 'main()' - Apply sandbox profile and execute program.
41  */
42 
43 int					/* O - Exit status */
main(int argc,char * argv[])44 main(int  argc,				/* I - Number of command-line args */
45      char *argv[])			/* I - Command-line arguments */
46 {
47   int		i;			/* Looping var */
48   const char	*opt;			/* Current option character */
49   uid_t		uid = getuid();		/* UID */
50   gid_t		gid = getgid();		/* GID */
51   int		niceval = 0;		/* Nice value */
52 #ifdef HAVE_SANDBOX_H
53   char		*sandbox_error = NULL;	/* Sandbox error, if any */
54 #endif /* HAVE_SANDBOX_H */
55 
56 
57  /*
58   * Parse command-line...
59   */
60 
61   for (i = 1; i < argc; i ++)
62   {
63     if (argv[i][0] == '-')
64     {
65       for (opt = argv[i] + 1; *opt; opt ++)
66       {
67         switch (*opt)
68         {
69           case 'g' : /* -g gid */
70               i ++;
71               if (i >= argc)
72                 usage();
73 
74               gid = (gid_t)atoi(argv[i]);
75               break;
76 
77           case 'n' : /* -n nice-value */
78               i ++;
79               if (i >= argc)
80                 usage();
81 
82               niceval = atoi(argv[i]);
83               break;
84 
85           case 'u' : /* -g gid */
86               i ++;
87               if (i >= argc)
88                 usage();
89 
90               uid = (uid_t)atoi(argv[i]);
91               break;
92 
93 	  default :
94 	      fprintf(stderr, "cups-exec: Unknown option '-%c'.\n", *opt);
95 	      usage();
96         }
97       }
98     }
99     else
100       break;
101   }
102 
103  /*
104   * Check that we have enough arguments...
105   */
106 
107   if ((i + 3) > argc)
108   {
109     fputs("cups-exec: Insufficient arguments.\n", stderr);
110     usage();
111   }
112 
113  /*
114   * Make sure side and back channel FDs are non-blocking...
115   */
116 
117   fcntl(3, F_SETFL, O_NDELAY);
118   fcntl(4, F_SETFL, O_NDELAY);
119 
120  /*
121   * Change UID, GID, and nice value...
122   */
123 
124   if (uid)
125     nice(niceval);
126 
127   if (!getuid())
128   {
129     if (setgid(gid))
130       exit(errno + 100);
131 
132     if (setgroups(1, &gid))
133       exit(errno + 100);
134 
135     if (uid && setuid(uid))
136       exit(errno + 100);
137   }
138 
139   umask(077);
140 
141 #ifdef HAVE_SANDBOX_H
142  /*
143   * Run in a separate security profile...
144   */
145 
146   if (strcmp(argv[i], "none") &&
147       sandbox_init(argv[i], SANDBOX_NAMED_EXTERNAL, &sandbox_error))
148   {
149     cups_file_t	*fp;			/* File */
150     char	line[1024];		/* Line from file */
151     int		linenum = 0;		/* Line number in file */
152 
153     fprintf(stderr, "DEBUG: sandbox_init failed: %s (%s)\n", sandbox_error,
154 	    strerror(errno));
155     sandbox_free_error(sandbox_error);
156 
157     if ((fp = cupsFileOpen(argv[i], "r")) != NULL)
158     {
159       while (cupsFileGets(fp, line, sizeof(line)))
160       {
161         linenum ++;
162         fprintf(stderr, "DEBUG: %4d  %s\n", linenum, line);
163       }
164       cupsFileClose(fp);
165     }
166 
167     return (100 + EINVAL);
168   }
169 #endif /* HAVE_SANDBOX_H */
170 
171  /*
172   * Execute the program...
173   */
174 
175   execv(argv[i + 1], argv + i + 2);
176 
177  /*
178   * If we get here, execv() failed...
179   */
180 
181   fprintf(stderr, "DEBUG: execv failed: %s\n", strerror(errno));
182   return (errno + 100);
183 }
184 
185 
186 /*
187  * 'usage()' - Show program usage.
188  */
189 
190 static void
usage(void)191 usage(void)
192 {
193   fputs("Usage: cups-exec [-g gid] [-n nice-value] [-u uid] /path/to/profile /path/to/program argv0 argv1 ... argvN\n", stderr);
194   exit(1);
195 }
196