Imported Upstream version 1.5.3
[platform/upstream/cups.git] / scheduler / cert.c
1 /*
2  * "$Id: cert.c 10262 2012-02-12 05:48:09Z mike $"
3  *
4  *   Authentication certificate routines for the CUPS scheduler.
5  *
6  *   Copyright 2007-2011 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       int       j;                      /* Looping var */
125
126 #  ifdef HAVE_MBR_UID_TO_UUID
127      /*
128       * On MacOS X, 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
244     RootCertTime = time(NULL);
245   }
246   else
247   {
248    /*
249     * CGI certificate...
250     */
251
252     fchmod(fd, 0400);
253     fchown(fd, User, Group);
254   }
255
256   DEBUG_printf(("ADD pid=%d, username=%s, cert=%s\n", pid, username,
257                 cert->certificate));
258
259   write(fd, cert->certificate, strlen(cert->certificate));
260   close(fd);
261
262  /*
263   * Add Kerberos credentials as needed...
264   */
265
266 #ifdef HAVE_GSSAPI
267   cert->ccache = (krb5_ccache)ccache;
268 #else
269   (void)ccache;
270 #endif /* HAVE_GSSAPI */
271
272  /*
273   * Insert the certificate at the front of the list...
274   */
275
276   cert->next = Certs;
277   Certs      = cert;
278 }
279
280
281 /*
282  * 'cupsdDeleteCert()' - Delete a single certificate.
283  */
284
285 void
286 cupsdDeleteCert(int pid)                /* I - Process ID */
287 {
288   cupsd_cert_t  *cert,                  /* Current certificate */
289                 *prev;                  /* Previous certificate */
290   char          filename[1024];         /* Certificate file */
291
292
293   for (prev = NULL, cert = Certs; cert != NULL; prev = cert, cert = cert->next)
294     if (cert->pid == pid)
295     {
296      /*
297       * Remove this certificate from the list...
298       */
299
300       cupsdLogMessage(CUPSD_LOG_DEBUG2,
301                       "cupsdDeleteCert: Removing certificate for PID %d", pid);
302
303       DEBUG_printf(("DELETE pid=%d, username=%s, cert=%s\n", cert->pid,
304                     cert->username, cert->certificate));
305
306       if (prev == NULL)
307         Certs = cert->next;
308       else
309         prev->next = cert->next;
310
311 #ifdef HAVE_GSSAPI
312      /*
313       * Release Kerberos credentials as needed...
314       */
315
316       if (cert->ccache)
317         krb5_cc_destroy(KerberosContext, cert->ccache);
318 #endif /* HAVE_GSSAPI */
319
320       free(cert);
321
322      /*
323       * Delete the file and return...
324       */
325
326       snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, pid);
327       if (unlink(filename))
328         cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
329
330       return;
331     }
332 }
333
334
335 /*
336  * 'cupsdDeleteAllCerts()' - Delete all certificates...
337  */
338
339 void
340 cupsdDeleteAllCerts(void)
341 {
342   cupsd_cert_t  *cert,                  /* Current certificate */
343                 *next;                  /* Next certificate */
344   char          filename[1024];         /* Certificate file */
345
346
347  /*
348   * Loop through each certificate, deleting them...
349   */
350
351   for (cert = Certs; cert != NULL; cert = next)
352   {
353    /*
354     * Delete the file...
355     */
356
357     snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, cert->pid);
358     if (unlink(filename))
359       cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
360
361    /*
362     * Free memory...
363     */
364
365     next = cert->next;
366     free(cert);
367   }
368
369   Certs        = NULL;
370   RootCertTime = 0;
371 }
372
373
374 /*
375  * 'cupsdFindCert()' - Find a certificate.
376  */
377
378 cupsd_cert_t *                          /* O - Matching certificate or NULL */
379 cupsdFindCert(const char *certificate)  /* I - Certificate */
380 {
381   cupsd_cert_t  *cert;                  /* Current certificate */
382
383
384   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert(certificate=%s)",
385                   certificate);
386   for (cert = Certs; cert != NULL; cert = cert->next)
387     if (!_cups_strcasecmp(certificate, cert->certificate))
388     {
389       cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Returning %s...",
390                       cert->username);
391       return (cert);
392     }
393
394   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Certificate not found!");
395
396   return (NULL);
397 }
398
399
400 /*
401  * 'cupsdInitCerts()' - Initialize the certificate "system" and root
402  *                      certificate.
403  */
404
405 void
406 cupsdInitCerts(void)
407 {
408 #ifndef HAVE_ARC4RANDOM
409   cups_file_t   *fp;                    /* /dev/random file */
410
411
412  /*
413   * Initialize the random number generator using the random device or
414   * the current time, as available...
415   */
416
417   if ((fp = cupsFileOpen("/dev/urandom", "rb")) == NULL)
418   {
419     struct timeval tod;                 /* Time of day */
420
421    /*
422     * Get the time in usecs and use it as the initial seed...
423     */
424
425     gettimeofday(&tod, NULL);
426
427     CUPS_SRAND((unsigned)(tod.tv_sec + tod.tv_usec));
428   }
429   else
430   {
431     unsigned    seed;                   /* Seed for random number generator */
432
433    /*
434     * Read 4 random characters from the random device and use
435     * them as the seed...
436     */
437
438     seed = cupsFileGetChar(fp);
439     seed = (seed << 8) | cupsFileGetChar(fp);
440     seed = (seed << 8) | cupsFileGetChar(fp);
441     CUPS_SRAND((seed << 8) | cupsFileGetChar(fp));
442
443     cupsFileClose(fp);
444   }
445 #endif /* !HAVE_ARC4RANDOM */
446
447  /*
448   * Create a root certificate and return...
449   */
450
451   if (!RunUser)
452     cupsdAddCert(0, "root", NULL);
453 }
454
455
456 /*
457  * End of "$Id: cert.c 10262 2012-02-12 05:48:09Z mike $".
458  */