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