Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
srohatgi01
GitHub Repository: srohatgi01/cups
Path: blob/master/scheduler/cert.c
1090 views
1
/*
2
* Authentication certificate routines for the CUPS scheduler.
3
*
4
* Copyright © 2021-2022 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
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(sizeof(cupsd_cert_t), 1)) == 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
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
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 */
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
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 */
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
457