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