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