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