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