1 /*
2 * Authentication certificate routines for the CUPS scheduler.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2016 by Apple Inc.
6 * Copyright © 1997-2006 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include "cupsd.h"
17 #ifdef HAVE_ACL_INIT
18 # include <sys/acl.h>
19 # ifdef HAVE_MEMBERSHIP_H
20 # include <membership.h>
21 # endif /* HAVE_MEMBERSHIP_H */
22 #endif /* HAVE_ACL_INIT */
23
24
25 /*
26 * Local functions...
27 */
28
29 static int ctcompare(const char *a, const char *b);
30
31
32 /*
33 * 'cupsdAddCert()' - Add a certificate.
34 */
35
36 void
cupsdAddCert(int pid,const char * username,int type)37 cupsdAddCert(int pid, /* I - Process ID */
38 const char *username, /* I - Username */
39 int type) /* I - AuthType for username */
40 {
41 int i; /* Looping var */
42 cupsd_cert_t *cert; /* Current certificate */
43 int fd; /* Certificate file */
44 char filename[1024]; /* Certificate filename */
45 static const char hex[] = "0123456789ABCDEF";
46 /* Hex constants... */
47
48
49 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddCert: Adding certificate for PID %d", pid);
50
51 /*
52 * Allocate memory for the certificate...
53 */
54
55 if ((cert = calloc(1, sizeof(cupsd_cert_t))) == NULL)
56 return;
57
58 /*
59 * Fill in the certificate information...
60 */
61
62 cert->pid = pid;
63 cert->type = type;
64 strlcpy(cert->username, username, sizeof(cert->username));
65
66 for (i = 0; i < 32; i ++)
67 cert->certificate[i] = hex[CUPS_RAND() & 15];
68
69 /*
70 * Save the certificate to a file readable only by the User and Group
71 * (or root and SystemGroup for PID == 0)...
72 */
73
74 snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, pid);
75 unlink(filename);
76
77 if ((fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400)) < 0)
78 {
79 cupsdLogMessage(CUPSD_LOG_ERROR,
80 "Unable to create certificate file %s - %s",
81 filename, strerror(errno));
82 free(cert);
83 return;
84 }
85
86 if (pid == 0)
87 {
88 #if defined(HAVE_ACL_INIT) && !CUPS_SNAP
89 acl_t acl; /* ACL information */
90 acl_entry_t entry; /* ACL entry */
91 acl_permset_t permset; /* Permissions */
92 # ifdef HAVE_MBR_UID_TO_UUID
93 uuid_t group; /* Group ID */
94 # endif /* HAVE_MBR_UID_TO_UUID */
95 static int acls_not_supported = 0;
96 /* Only warn once */
97 #endif /* HAVE_ACL_INIT && !CUPS_SNAP */
98
99
100 /*
101 * Root certificate...
102 */
103
104 fchmod(fd, 0440);
105
106 /* ACLs do not work when cupsd is running in a Snap, and certificates
107 need root as group owner to be only accessible for CUPS and not the
108 unprivileged sub-processes */
109 #if CUPS_SNAP
110 fchown(fd, RunUser, 0);
111 #else
112 fchown(fd, RunUser, SystemGroupIDs[0]);
113
114 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddCert: NumSystemGroups=%d", NumSystemGroups);
115
116 # ifdef HAVE_ACL_INIT
117 if (NumSystemGroups > 1)
118 {
119 /*
120 * Set POSIX ACLs for the root certificate so that all system
121 * groups can access it...
122 */
123
124 int j; /* Looping var */
125
126 # ifdef HAVE_MBR_UID_TO_UUID
127 /*
128 * On macOS, ACLs use UUIDs instead of GIDs...
129 */
130
131 acl = acl_init(NumSystemGroups - 1);
132
133 for (i = 1; i < NumSystemGroups; i ++)
134 {
135 /*
136 * Add each group ID to the ACL...
137 */
138
139 for (j = 0; j < i; j ++)
140 if (SystemGroupIDs[j] == SystemGroupIDs[i])
141 break;
142
143 if (j < i)
144 continue; /* Skip duplicate groups */
145
146 acl_create_entry(&acl, &entry);
147 acl_get_permset(entry, &permset);
148 acl_add_perm(permset, ACL_READ_DATA);
149 acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
150 mbr_gid_to_uuid((gid_t)SystemGroupIDs[i], group);
151 acl_set_qualifier(entry, &group);
152 acl_set_permset(entry, permset);
153 }
154
155 # else
156 /*
157 * POSIX ACLs need permissions for owner, group, other, and mask
158 * in addition to the rest of the system groups...
159 */
160
161 acl = acl_init(NumSystemGroups + 3);
162
163 /* Owner */
164 acl_create_entry(&acl, &entry);
165 acl_get_permset(entry, &permset);
166 acl_add_perm(permset, ACL_READ);
167 acl_set_tag_type(entry, ACL_USER_OBJ);
168 acl_set_permset(entry, permset);
169
170 /* Group */
171 acl_create_entry(&acl, &entry);
172 acl_get_permset(entry, &permset);
173 acl_add_perm(permset, ACL_READ);
174 acl_set_tag_type(entry, ACL_GROUP_OBJ);
175 acl_set_permset(entry, permset);
176
177 /* Others */
178 acl_create_entry(&acl, &entry);
179 acl_get_permset(entry, &permset);
180 acl_add_perm(permset, 0);
181 acl_set_tag_type(entry, ACL_OTHER);
182 acl_set_permset(entry, permset);
183
184 /* Mask */
185 acl_create_entry(&acl, &entry);
186 acl_get_permset(entry, &permset);
187 acl_add_perm(permset, ACL_READ);
188 acl_set_tag_type(entry, ACL_MASK);
189 acl_set_permset(entry, permset);
190
191 for (i = 1; i < NumSystemGroups; i ++)
192 {
193 /*
194 * Add each group ID to the ACL...
195 */
196
197 for (j = 0; j < i; j ++)
198 if (SystemGroupIDs[j] == SystemGroupIDs[i])
199 break;
200
201 if (j < i)
202 continue; /* Skip duplicate groups */
203
204 acl_create_entry(&acl, &entry);
205 acl_get_permset(entry, &permset);
206 acl_add_perm(permset, ACL_READ);
207 acl_set_tag_type(entry, ACL_GROUP);
208 acl_set_qualifier(entry, SystemGroupIDs + i);
209 acl_set_permset(entry, permset);
210 }
211
212 if (acl_valid(acl))
213 {
214 char *text, *textptr; /* Temporary string */
215
216 cupsdLogMessage(CUPSD_LOG_ERROR, "ACL did not validate: %s",
217 strerror(errno));
218 text = acl_to_text(acl, NULL);
219 for (textptr = strchr(text, '\n');
220 textptr;
221 textptr = strchr(textptr + 1, '\n'))
222 *textptr = ',';
223
224 cupsdLogMessage(CUPSD_LOG_ERROR, "ACL: %s", text);
225 acl_free(text);
226 }
227 # endif /* HAVE_MBR_UID_TO_UUID */
228
229 if (acl_set_fd(fd, acl))
230 {
231 if (errno != EOPNOTSUPP || !acls_not_supported)
232 cupsdLogMessage(CUPSD_LOG_ERROR,
233 "Unable to set ACLs on root certificate \"%s\" - %s",
234 filename, strerror(errno));
235
236 if (errno == EOPNOTSUPP)
237 acls_not_supported = 1;
238 }
239
240 acl_free(acl);
241 }
242 # endif /* HAVE_ACL_INIT */
243 #endif /* CUPS_SNAP */
244
245 RootCertTime = time(NULL);
246 }
247 else
248 {
249 /*
250 * CGI certificate...
251 */
252
253 fchmod(fd, 0400);
254 fchown(fd, User, Group);
255 }
256
257 write(fd, cert->certificate, strlen(cert->certificate));
258 close(fd);
259
260 /*
261 * Insert the certificate at the front of the list...
262 */
263
264 cert->next = Certs;
265 Certs = cert;
266 }
267
268
269 /*
270 * 'cupsdDeleteCert()' - Delete a single certificate.
271 */
272
273 void
cupsdDeleteCert(int pid)274 cupsdDeleteCert(int pid) /* I - Process ID */
275 {
276 cupsd_cert_t *cert, /* Current certificate */
277 *prev; /* Previous certificate */
278 char filename[1024]; /* Certificate file */
279
280
281 for (prev = NULL, cert = Certs; cert != NULL; prev = cert, cert = cert->next)
282 if (cert->pid == pid)
283 {
284 /*
285 * Remove this certificate from the list...
286 */
287
288 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeleteCert: Removing certificate for PID %d.", pid);
289
290 if (prev == NULL)
291 Certs = cert->next;
292 else
293 prev->next = cert->next;
294
295 free(cert);
296
297 /*
298 * Delete the file and return...
299 */
300
301 snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, pid);
302 if (unlink(filename))
303 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
304
305 return;
306 }
307 }
308
309
310 /*
311 * 'cupsdDeleteAllCerts()' - Delete all certificates...
312 */
313
314 void
cupsdDeleteAllCerts(void)315 cupsdDeleteAllCerts(void)
316 {
317 cupsd_cert_t *cert, /* Current certificate */
318 *next; /* Next certificate */
319 char filename[1024]; /* Certificate file */
320
321
322 /*
323 * Loop through each certificate, deleting them...
324 */
325
326 for (cert = Certs; cert != NULL; cert = next)
327 {
328 /*
329 * Delete the file...
330 */
331
332 snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, cert->pid);
333 if (unlink(filename))
334 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
335
336 /*
337 * Free memory...
338 */
339
340 next = cert->next;
341 free(cert);
342 }
343
344 Certs = NULL;
345 RootCertTime = 0;
346 }
347
348
349 /*
350 * 'cupsdFindCert()' - Find a certificate.
351 */
352
353 cupsd_cert_t * /* O - Matching certificate or NULL */
cupsdFindCert(const char * certificate)354 cupsdFindCert(const char *certificate) /* I - Certificate */
355 {
356 cupsd_cert_t *cert; /* Current certificate */
357
358
359 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert(certificate=%s)", certificate);
360 for (cert = Certs; cert != NULL; cert = cert->next)
361 if (!ctcompare(certificate, cert->certificate))
362 {
363 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Returning \"%s\".", cert->username);
364 return (cert);
365 }
366
367 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Certificate not found.");
368
369 return (NULL);
370 }
371
372
373 /*
374 * 'cupsdInitCerts()' - Initialize the certificate "system" and root
375 * certificate.
376 */
377
378 void
cupsdInitCerts(void)379 cupsdInitCerts(void)
380 {
381 #ifndef HAVE_ARC4RANDOM
382 cups_file_t *fp; /* /dev/random file */
383
384
385 /*
386 * Initialize the random number generator using the random device or
387 * the current time, as available...
388 */
389
390 if ((fp = cupsFileOpen("/dev/urandom", "rb")) == NULL)
391 {
392 struct timeval tod; /* Time of day */
393
394 /*
395 * Get the time in usecs and use it as the initial seed...
396 */
397
398 gettimeofday(&tod, NULL);
399
400 CUPS_SRAND((unsigned)(tod.tv_sec + tod.tv_usec));
401 }
402 else
403 {
404 unsigned seed; /* Seed for random number generator */
405
406 /*
407 * Read 4 random characters from the random device and use
408 * them as the seed...
409 */
410
411 seed = (unsigned)cupsFileGetChar(fp);
412 seed = (seed << 8) | (unsigned)cupsFileGetChar(fp);
413 seed = (seed << 8) | (unsigned)cupsFileGetChar(fp);
414 CUPS_SRAND((seed << 8) | (unsigned)cupsFileGetChar(fp));
415
416 cupsFileClose(fp);
417 }
418 #endif /* !HAVE_ARC4RANDOM */
419
420 /*
421 * Create a root certificate and return...
422 */
423
424 if (!RunUser)
425 cupsdAddCert(0, "root", cupsdDefaultAuthType());
426 }
427
428
429 /*
430 * 'ctcompare()' - Compare two strings in constant time.
431 */
432
433 static int /* O - 0 on match, non-zero on non-match */
ctcompare(const char * a,const char * b)434 ctcompare(const char *a, /* I - First string */
435 const char *b) /* I - Second string */
436 {
437 int result = 0; /* Result */
438
439
440 while (*a && *b)
441 {
442 result |= *a ^ *b;
443 a ++;
444 b ++;
445 }
446
447 /*
448 * The while loop finishes when *a == '\0' or *b == '\0'
449 * so after the while loop either both *a and *b == '\0',
450 * or one points inside a string, so when we apply bitwise OR on *a,
451 * *b and result, we get a non-zero return value if the compared strings don't match.
452 */
453
454 return (result | *a | *b);
455 }
456