1/*
2 * "$Id: cert.c 12035 2014-07-16 19:40:05Z msweet $"
3 *
4 * Authentication certificate routines for the CUPS scheduler.
5 *
6 * Copyright 2007-2014 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
16/*
17 * Include necessary headers...
18 */
19
20#include "cupsd.h"
21#ifdef HAVE_ACL_INIT
22#  include <sys/acl.h>
23#  ifdef HAVE_MEMBERSHIP_H
24#    include <membership.h>
25#  endif /* HAVE_MEMBERSHIP_H */
26#endif /* HAVE_ACL_INIT */
27
28
29/*
30 * 'cupsdAddCert()' - Add a certificate.
31 */
32
33void
34cupsdAddCert(int        pid,		/* I - Process ID */
35             const char *username,	/* I - Username */
36             int        type)		/* I - AuthType for username */
37{
38  int		i;			/* Looping var */
39  cupsd_cert_t	*cert;			/* Current certificate */
40  int		fd;			/* Certificate file */
41  char		filename[1024];		/* Certificate filename */
42  static const char hex[] = "0123456789ABCDEF";
43					/* Hex constants... */
44
45
46  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddCert: Adding certificate for PID %d", pid);
47
48 /*
49  * Allocate memory for the certificate...
50  */
51
52  if ((cert = calloc(sizeof(cupsd_cert_t), 1)) == NULL)
53    return;
54
55 /*
56  * Fill in the certificate information...
57  */
58
59  cert->pid  = pid;
60  cert->type = type;
61  strlcpy(cert->username, username, sizeof(cert->username));
62
63  for (i = 0; i < 32; i ++)
64    cert->certificate[i] = hex[CUPS_RAND() & 15];
65
66 /*
67  * Save the certificate to a file readable only by the User and Group
68  * (or root and SystemGroup for PID == 0)...
69  */
70
71  snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, pid);
72  unlink(filename);
73
74  if ((fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400)) < 0)
75  {
76    cupsdLogMessage(CUPSD_LOG_ERROR,
77                    "Unable to create certificate file %s - %s",
78                    filename, strerror(errno));
79    free(cert);
80    return;
81  }
82
83  if (pid == 0)
84  {
85#ifdef HAVE_ACL_INIT
86    acl_t		acl;		/* ACL information */
87    acl_entry_t		entry;		/* ACL entry */
88    acl_permset_t	permset;	/* Permissions */
89#  ifdef HAVE_MBR_UID_TO_UUID
90    uuid_t		group;		/* Group ID */
91#  endif /* HAVE_MBR_UID_TO_UUID */
92    static int		acls_not_supported = 0;
93					/* Only warn once */
94#endif /* HAVE_ACL_INIT */
95
96
97   /*
98    * Root certificate...
99    */
100
101    fchmod(fd, 0440);
102    fchown(fd, RunUser, SystemGroupIDs[0]);
103
104    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddCert: NumSystemGroups=%d",
105                    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 X, 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
266void
267cupsdDeleteCert(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,
282                      "cupsdDeleteCert: Removing certificate for PID %d", pid);
283
284      DEBUG_printf(("DELETE pid=%d, username=%s, cert=%s\n", cert->pid,
285                    cert->username, cert->certificate));
286
287      if (prev == NULL)
288        Certs = cert->next;
289      else
290        prev->next = cert->next;
291
292      free(cert);
293
294     /*
295      * Delete the file and return...
296      */
297
298      snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, pid);
299      if (unlink(filename))
300	cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
301
302      return;
303    }
304}
305
306
307/*
308 * 'cupsdDeleteAllCerts()' - Delete all certificates...
309 */
310
311void
312cupsdDeleteAllCerts(void)
313{
314  cupsd_cert_t	*cert,			/* Current certificate */
315		*next;			/* Next certificate */
316  char		filename[1024];		/* Certificate file */
317
318
319 /*
320  * Loop through each certificate, deleting them...
321  */
322
323  for (cert = Certs; cert != NULL; cert = next)
324  {
325   /*
326    * Delete the file...
327    */
328
329    snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, cert->pid);
330    if (unlink(filename))
331      cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
332
333   /*
334    * Free memory...
335    */
336
337    next = cert->next;
338    free(cert);
339  }
340
341  Certs        = NULL;
342  RootCertTime = 0;
343}
344
345
346/*
347 * 'cupsdFindCert()' - Find a certificate.
348 */
349
350cupsd_cert_t *				/* O - Matching certificate or NULL */
351cupsdFindCert(const char *certificate)	/* I - Certificate */
352{
353  cupsd_cert_t	*cert;			/* Current certificate */
354
355
356  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert(certificate=%s)",
357                  certificate);
358  for (cert = Certs; cert != NULL; cert = cert->next)
359    if (!_cups_strcasecmp(certificate, cert->certificate))
360    {
361      cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Returning %s...",
362                      cert->username);
363      return (cert);
364    }
365
366  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Certificate not found!");
367
368  return (NULL);
369}
370
371
372/*
373 * 'cupsdInitCerts()' - Initialize the certificate "system" and root
374 *                      certificate.
375 */
376
377void
378cupsdInitCerts(void)
379{
380#ifndef HAVE_ARC4RANDOM
381  cups_file_t	*fp;			/* /dev/random file */
382
383
384 /*
385  * Initialize the random number generator using the random device or
386  * the current time, as available...
387  */
388
389  if ((fp = cupsFileOpen("/dev/urandom", "rb")) == NULL)
390  {
391    struct timeval tod;			/* Time of day */
392
393   /*
394    * Get the time in usecs and use it as the initial seed...
395    */
396
397    gettimeofday(&tod, NULL);
398
399    CUPS_SRAND((unsigned)(tod.tv_sec + tod.tv_usec));
400  }
401  else
402  {
403    unsigned	seed;			/* Seed for random number generator */
404
405   /*
406    * Read 4 random characters from the random device and use
407    * them as the seed...
408    */
409
410    seed = (unsigned)cupsFileGetChar(fp);
411    seed = (seed << 8) | (unsigned)cupsFileGetChar(fp);
412    seed = (seed << 8) | (unsigned)cupsFileGetChar(fp);
413    CUPS_SRAND((seed << 8) | (unsigned)cupsFileGetChar(fp));
414
415    cupsFileClose(fp);
416  }
417#endif /* !HAVE_ARC4RANDOM */
418
419 /*
420  * Create a root certificate and return...
421  */
422
423  if (!RunUser)
424    cupsdAddCert(0, "root", cupsdDefaultAuthType());
425}
426
427
428/*
429 * End of "$Id: cert.c 12035 2014-07-16 19:40:05Z msweet $".
430 */
431