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