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