]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/auth.c
a3ca5ef6b038e7f0836f1bf0ee1b11c01ffceb8d
[thirdparty/cups.git] / scheduler / auth.c
1 /*
2 * Authorization routines for the CUPS scheduler.
3 *
4 * Copyright © 2020-2025 by OpenPrinting.
5 * Copyright © 2007-2019 by Apple Inc.
6 * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
7 *
8 * This file contains Kerberos support code, copyright 2006 by
9 * Jelmer Vernooij.
10 *
11 * Licensed under Apache License v2.0. See the file "LICENSE" for more
12 * information.
13 */
14
15 #include "cupsd.h"
16 #include <grp.h>
17 #if HAVE_LIBPAM
18 # ifdef HAVE_PAM_PAM_APPL_H
19 # include <pam/pam_appl.h>
20 # else
21 # include <security/pam_appl.h>
22 # endif /* HAVE_PAM_PAM_APPL_H */
23 #endif /* HAVE_LIBPAM */
24 #ifdef HAVE_MEMBERSHIP_H
25 # include <membership.h>
26 #endif /* HAVE_MEMBERSHIP_H */
27 #ifdef HAVE_AUTHORIZATION_H
28 # include <Security/AuthorizationTags.h>
29 #endif /* HAVE_AUTHORIZATION_H */
30 #ifdef HAVE_SYS_PARAM_H
31 # include <sys/param.h>
32 #endif /* HAVE_SYS_PARAM_H */
33 #ifdef HAVE_SYS_UCRED_H
34 # include <sys/ucred.h>
35 typedef struct xucred cupsd_ucred_t;
36 # define CUPSD_UCRED_UID(c) (c).cr_uid
37 #else
38 # ifndef __OpenBSD__
39 typedef struct ucred cupsd_ucred_t;
40 # else
41 typedef struct sockpeercred cupsd_ucred_t;
42 # endif
43 # define CUPSD_UCRED_UID(c) (c).uid
44 #endif /* HAVE_SYS_UCRED_H */
45 #ifdef HAVE_LIBAPPARMOR
46 # include <sys/apparmor.h>
47 #endif /* HAVE_LIBAPPARMOR */
48 #ifdef HAVE_LIBSNAPDGLIB
49 # include <glib.h>
50 # include <snapd-glib/snapd-glib.h>
51 #endif /* HAVE_LIBSNAPDGLIB */
52
53
54 /*
55 * Local functions...
56 */
57
58 static int check_admin_access(cupsd_client_t *con);
59 #ifdef HAVE_AUTHORIZATION_H
60 static int check_authref(cupsd_client_t *con, const char *right);
61 #endif /* HAVE_AUTHORIZATION_H */
62 static int compare_locations(cupsd_location_t *a, cupsd_location_t *b, void *data);
63 static int compare_ogroups(cupsd_ogroup_t *a, cupsd_ogroup_t *b, void *data);
64 static cupsd_authmask_t *copy_authmask(cupsd_authmask_t *am, void *data);
65 static void free_authmask(cupsd_authmask_t *am, void *data);
66 static void free_ogroup(cupsd_ogroup_t *og, void *data);
67 static int load_ogroup(cupsd_ogroup_t *og, struct stat *fileinfo);
68 #if HAVE_LIBPAM
69 static int pam_func(int, const struct pam_message **,
70 struct pam_response **, void *);
71 #endif /* HAVE_LIBPAM */
72
73
74 /*
75 * Local structures...
76 */
77
78 #if HAVE_LIBPAM
79 typedef struct cupsd_authdata_s /**** Authentication data ****/
80 {
81 char username[HTTP_MAX_VALUE], /* Username string */
82 password[HTTP_MAX_VALUE]; /* Password string */
83 } cupsd_authdata_t;
84 #endif /* HAVE_LIBPAM */
85
86
87 /*
88 * 'cupsdAddIPMask()' - Add an IP address authorization mask.
89 */
90
91 int /* O - 1 on success, 0 on failure */
92 cupsdAddIPMask(
93 cups_array_t **masks, /* IO - Masks array (created as needed) */
94 const unsigned address[4], /* I - IP address */
95 const unsigned netmask[4]) /* I - IP netmask */
96 {
97 cupsd_authmask_t temp; /* New host/domain mask */
98
99
100 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddIPMask(masks=%p(%p), address=%x:%x:%x:%x, netmask=%x:%x:%x:%x)", (void *)masks, (void *)*masks, address[0], address[1], address[2], address[3], netmask[0], netmask[1], netmask[2], netmask[3]);
101
102 temp.type = CUPSD_AUTH_IP;
103 memcpy(temp.mask.ip.address, address, sizeof(temp.mask.ip.address));
104 memcpy(temp.mask.ip.netmask, netmask, sizeof(temp.mask.ip.netmask));
105
106 /*
107 * Create the masks array as needed and add...
108 */
109
110 if (!*masks)
111 *masks = cupsArrayNew3(NULL, NULL, NULL, 0,
112 (cups_acopy_func_t)copy_authmask,
113 (cups_afree_func_t)free_authmask);
114
115 return (cupsArrayAdd(*masks, &temp));
116 }
117
118
119 /*
120 * 'cupsdAddLocation()' - Add a location for authorization.
121 */
122
123 void
124 cupsdAddLocation(cupsd_location_t *loc) /* I - Location to add */
125 {
126 /*
127 * Make sure the locations array is created...
128 */
129
130 if (!Locations)
131 Locations = cupsArrayNew3((cups_array_func_t)compare_locations, NULL,
132 (cups_ahash_func_t)NULL, 0,
133 (cups_acopy_func_t)NULL,
134 (cups_afree_func_t)cupsdFreeLocation);
135
136 if (Locations)
137 {
138 cupsArrayAdd(Locations, loc);
139
140 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddLocation: Added location \"%s\"", loc->location ? loc->location : "(null)");
141 }
142 }
143
144
145 /*
146 * 'cupsdAddName()' - Add a name to a location...
147 */
148
149 void
150 cupsdAddName(cupsd_location_t *loc, /* I - Location to add to */
151 char *name) /* I - Name to add */
152 {
153 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddName(loc=%p, name=\"%s\")", (void *)loc, name);
154
155 if (!loc->names)
156 loc->names = cupsArrayNew3(NULL, NULL, NULL, 0,
157 (cups_acopy_func_t)_cupsArrayStrdup,
158 (cups_afree_func_t)_cupsArrayFree);
159
160 if (!cupsArrayAdd(loc->names, name))
161 {
162 cupsdLogMessage(CUPSD_LOG_ERROR,
163 "Unable to duplicate name for location %s: %s",
164 loc->location ? loc->location : "nil", strerror(errno));
165 return;
166 }
167 }
168
169
170 /*
171 * 'cupsdAddNameMask()' - Add a host or interface name authorization mask.
172 */
173
174 int /* O - 1 on success, 0 on failure */
175 cupsdAddNameMask(cups_array_t **masks, /* IO - Masks array (created as needed) */
176 char *name) /* I - Host or interface name */
177 {
178 cupsd_authmask_t temp; /* New host/domain mask */
179 char ifname[32], /* Interface name */
180 *ifptr; /* Pointer to end of name */
181
182
183 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddNameMask(masks=%p(%p), name=\"%s\")", (void *)masks, (void *)*masks, name);
184
185 if (!_cups_strcasecmp(name, "@LOCAL"))
186 {
187 /*
188 * Deny *interface*...
189 */
190
191 temp.type = CUPSD_AUTH_INTERFACE;
192 temp.mask.name.name = (char *)"*";
193 }
194 else if (!_cups_strncasecmp(name, "@IF(", 4))
195 {
196 /*
197 * Deny *interface*...
198 */
199
200 cupsCopyString(ifname, name + 4, sizeof(ifname));
201
202 ifptr = ifname + strlen(ifname) - 1;
203
204 if (ifptr >= ifname && *ifptr == ')')
205 {
206 *ifptr = '\0';
207 }
208
209 temp.type = CUPSD_AUTH_INTERFACE;
210 temp.mask.name.name = ifname;
211 }
212 else
213 {
214 /*
215 * Deny name...
216 */
217
218 if (*name == '*')
219 name ++;
220
221 temp.type = CUPSD_AUTH_NAME;
222 temp.mask.name.name = (char *)name;
223 }
224
225 /*
226 * Set the name length...
227 */
228
229 temp.mask.name.length = strlen(temp.mask.name.name);
230
231 /*
232 * Create the masks array as needed and add...
233 */
234
235 if (!*masks)
236 *masks = cupsArrayNew3(NULL, NULL, NULL, 0,
237 (cups_acopy_func_t)copy_authmask,
238 (cups_afree_func_t)free_authmask);
239
240 return (cupsArrayAdd(*masks, &temp));
241 }
242
243
244 /*
245 * 'cupsdAddOAuthGroup()' - Add an OAuth group file.
246 */
247
248 int /* O - 1 on success, 0 on error */
249 cupsdAddOAuthGroup(const char *name, /* I - Group name */
250 const char *filename)/* I - Group filename */
251 {
252 cupsd_ogroup_t *og; /* Group */
253 struct stat fileinfo; /* File information */
254
255
256 /*
257 * Check OAuth group file...
258 */
259
260 if (stat(filename, &fileinfo))
261 {
262 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to access OAuthGroup %s file \"%s\": %s", name, filename, strerror(errno));
263 return (0);
264 }
265
266 /*
267 * Create the new group...
268 */
269
270 if (!OAuthGroups)
271 {
272 /*
273 * Create groups array...
274 */
275
276 if ((OAuthGroups = cupsArrayNew3((cups_array_cb_t)compare_ogroups, /*d*/NULL, /*h*/NULL, /*hsize*/0, /*cf*/NULL, (cups_afree_cb_t)free_ogroup)) == NULL)
277 {
278 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for OAuthGroup array: %s", strerror(errno));
279 return (0);
280 }
281 }
282
283 if ((og = (cupsd_ogroup_t *)calloc(1, sizeof(cupsd_ogroup_t))) == NULL)
284 {
285 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for OAuthGroup %s: %s", name, strerror(errno));
286 return (0);
287 }
288
289 if ((og->name = strdup(name)) == NULL || (og->filename = strdup(filename)) == NULL)
290 {
291 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for OAuthGroup %s: %s", name, strerror(errno));
292 free_ogroup(og, NULL);
293 return (0);
294 }
295
296 /*
297 * Add the group to the array...
298 */
299
300 cupsArrayAdd(OAuthGroups, og);
301
302 /*
303 * Load the group and return...
304 */
305
306 return (load_ogroup(og, &fileinfo));
307 }
308
309
310 /*
311 * 'cupsdAuthorize()' - Validate any authorization credentials.
312 */
313
314 void
315 cupsdAuthorize(cupsd_client_t *con) /* I - Client connection */
316 {
317 int type; /* Authentication type */
318 const char *authorization; /* Pointer into Authorization string */
319 char *ptr, /* Pointer into string */
320 bearer[4096], /* CUPS_BEARER cookie string */
321 username[HTTP_MAX_VALUE],
322 /* Username string */
323 password[HTTP_MAX_VALUE];
324 /* Password string */
325 cupsd_cert_t *localuser; /* Certificate username */
326
327
328 /*
329 * Locate the best matching location so we know what kind of
330 * authentication to expect...
331 */
332
333 con->best = cupsdFindBest(con->uri, httpGetState(con->http));
334 con->type = CUPSD_AUTH_NONE;
335
336 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "con->uri=\"%s\", con->best=%p(%s)", con->uri, (void *)con->best, con->best ? con->best->location : "");
337
338 if (con->best && con->best->type != CUPSD_AUTH_NONE)
339 {
340 if (con->best->type == CUPSD_AUTH_DEFAULT)
341 type = cupsdDefaultAuthType();
342 else
343 type = con->best->type;
344 }
345 else
346 type = cupsdDefaultAuthType();
347
348 /*
349 * Decode the Authorization string...
350 */
351
352 authorization = httpGetField(con->http, HTTP_FIELD_AUTHORIZATION);
353
354 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cookie=\"%s\"", httpGetCookie(con->http));
355
356 if (!*authorization && httpGetCookieValue(con->http, "CUPS_BEARER", bearer, sizeof(bearer)) && bearer[0])
357 authorization = "Bearer COOKIE";
358
359 username[0] = '\0';
360 password[0] = '\0';
361
362 #ifdef HAVE_GSSAPI
363 con->gss_uid = 0;
364 #endif /* HAVE_GSSAPI */
365
366 #ifdef HAVE_AUTHORIZATION_H
367 if (con->authref)
368 {
369 AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
370 con->authref = NULL;
371 }
372 #endif /* HAVE_AUTHORIZATION_H */
373
374 if (!*authorization)
375 {
376 /*
377 * No authorization data provided, return early...
378 */
379
380 cupsdLogClient(con, CUPSD_LOG_DEBUG, "No authentication data provided.");
381 return;
382 }
383 #ifdef HAVE_AUTHORIZATION_H
384 else if (!strncmp(authorization, "AuthRef ", 8) &&
385 httpAddrLocalhost(httpGetAddress(con->http)))
386 {
387 OSStatus status; /* Status */
388 char authdata[HTTP_MAX_VALUE];
389 /* Nonce value from client */
390 int authlen; /* Auth string length */
391 AuthorizationItemSet *authinfo; /* Authorization item set */
392
393 /*
394 * Get the Authorization Services data...
395 */
396
397 authorization += 8;
398 while (isspace(*authorization & 255))
399 authorization ++;
400
401 authlen = sizeof(authdata);
402 httpDecode64_2(authdata, &authlen, authorization);
403
404 if (authlen != kAuthorizationExternalFormLength)
405 {
406 cupsdLogClient(con, CUPSD_LOG_ERROR, "External Authorization reference size is incorrect.");
407 return;
408 }
409
410 if ((status = AuthorizationCreateFromExternalForm((AuthorizationExternalForm *)authdata, &con->authref)) != 0)
411 {
412 cupsdLogClient(con, CUPSD_LOG_ERROR, "AuthorizationCreateFromExternalForm returned %d", (int)status);
413 return;
414 }
415
416 username[0] = '\0';
417
418 if (!AuthorizationCopyInfo(con->authref, kAuthorizationEnvironmentUsername,
419 &authinfo))
420 {
421 if (authinfo->count == 1 && authinfo->items[0].value &&
422 authinfo->items[0].valueLength >= 2)
423 {
424 cupsCopyString(username, authinfo->items[0].value, sizeof(username));
425
426 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Authorized as \"%s\" using AuthRef.", username);
427 }
428
429 AuthorizationFreeItemSet(authinfo);
430 }
431
432 if (!username[0])
433 {
434 /*
435 * No username in AuthRef, grab username using peer credentials...
436 */
437
438 struct passwd *pwd; /* Password entry for this user */
439 cupsd_ucred_t peercred; /* Peer credentials */
440 socklen_t peersize; /* Size of peer credentials */
441
442 peersize = sizeof(peercred);
443
444 if (getsockopt(httpGetFd(con->http), 0, LOCAL_PEERCRED, &peercred, &peersize))
445 {
446 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to get peer credentials - %s", strerror(errno));
447 return;
448 }
449
450 if ((pwd = getpwuid(CUPSD_UCRED_UID(peercred))) == NULL)
451 {
452 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to find UID %d for peer credentials.", (int)CUPSD_UCRED_UID(peercred));
453 return;
454 }
455
456 cupsCopyString(username, pwd->pw_name, sizeof(username));
457
458 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Authorized as \"%s\" using AuthRef + PeerCred.", username);
459 }
460
461 con->type = CUPSD_AUTH_BASIC;
462 }
463 #endif /* HAVE_AUTHORIZATION_H */
464 #if defined(SO_PEERCRED) && defined(AF_LOCAL)
465 else if (!strncmp(authorization, "PeerCred ", 9) &&
466 con->http->hostaddr->addr.sa_family == AF_LOCAL && con->best)
467 {
468 /*
469 * Use peer credentials from domain socket connection...
470 */
471
472 struct passwd *pwd; /* Password entry for this user */
473 cupsd_ucred_t peercred; /* Peer credentials */
474 socklen_t peersize; /* Size of peer credentials */
475 #ifdef HAVE_AUTHORIZATION_H
476 const char *name; /* Authorizing name */
477 int no_peer = 0; /* Don't allow peer credentials? */
478
479 /*
480 * See if we should allow peer credentials...
481 */
482
483 for (name = (char *)cupsArrayFirst(con->best->names);
484 name;
485 name = (char *)cupsArrayNext(con->best->names))
486 {
487 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) ||
488 !_cups_strcasecmp(name, "@SYSTEM"))
489 {
490 /* Normally don't want peer credentials if we need an auth key... */
491 no_peer = 1;
492 }
493 else if (!_cups_strcasecmp(name, "@OWNER"))
494 {
495 /* but if @OWNER is present then we allow it... */
496 no_peer = 0;
497 break;
498 }
499 }
500
501 if (no_peer)
502 {
503 cupsdLogClient(con, CUPSD_LOG_ERROR, "PeerCred authentication not allowed for resource per AUTHKEY policy.");
504 return;
505 }
506 #endif /* HAVE_AUTHORIZATION_H */
507
508 if ((pwd = getpwnam(authorization + 9)) == NULL)
509 {
510 cupsdLogClient(con, CUPSD_LOG_ERROR, "User \"%s\" does not exist.", authorization + 9);
511 return;
512 }
513
514 peersize = sizeof(peercred);
515
516 # ifdef __APPLE__
517 if (getsockopt(httpGetFd(con->http), 0, LOCAL_PEERCRED, &peercred, &peersize))
518 # else
519 if (getsockopt(httpGetFd(con->http), SOL_SOCKET, SO_PEERCRED, &peercred, &peersize))
520 # endif /* __APPLE__ */
521 {
522 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to get peer credentials - %s", strerror(errno));
523 return;
524 }
525
526 if (pwd->pw_uid != CUPSD_UCRED_UID(peercred))
527 {
528 cupsdLogClient(con, CUPSD_LOG_ERROR, "Invalid peer credentials for \"%s\" - got %d, expected %d.", authorization + 9, CUPSD_UCRED_UID(peercred), pwd->pw_uid);
529 # ifdef HAVE_SYS_UCRED_H
530 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cr_version=%d", peercred.cr_version);
531 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cr_uid=%d", peercred.cr_uid);
532 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cr_ngroups=%d", peercred.cr_ngroups);
533 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cr_groups[0]=%d", peercred.cr_groups[0]);
534 # endif /* HAVE_SYS_UCRED_H */
535 return;
536 }
537
538 cupsCopyString(username, authorization + 9, sizeof(username));
539
540 # ifdef HAVE_GSSAPI
541 con->gss_uid = CUPSD_UCRED_UID(peercred);
542 # endif /* HAVE_GSSAPI */
543
544 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Authorized as %s using PeerCred.", username);
545
546 con->type = CUPSD_AUTH_BASIC;
547 }
548 #endif /* SO_PEERCRED && AF_LOCAL */
549 else if (!strncmp(authorization, "Local", 5) &&
550 httpAddrLocalhost(httpGetAddress(con->http)))
551 {
552 /*
553 * Get Local certificate authentication data...
554 */
555
556 authorization += 5;
557 while (isspace(*authorization & 255))
558 authorization ++;
559
560 if ((localuser = cupsdFindCert(authorization)) == NULL)
561 {
562 cupsdLogClient(con, CUPSD_LOG_ERROR, "Local authentication certificate not found.");
563 return;
564 }
565
566 cupsCopyString(username, localuser->username, sizeof(username));
567 con->type = localuser->type;
568
569 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Authorized as %s using Local.", username);
570 }
571 else if (!strncmp(authorization, "Basic ", 6))
572 {
573 /*
574 * Get the Basic authentication data...
575 */
576
577 int userlen; /* Username:password length */
578
579 authorization += 5;
580 while (isspace(*authorization & 255))
581 authorization ++;
582
583 userlen = sizeof(username);
584 httpDecode64_2(username, &userlen, authorization);
585
586 /*
587 * Pull the username and password out...
588 */
589
590 if ((ptr = strchr(username, ':')) == NULL)
591 {
592 cupsdLogClient(con, CUPSD_LOG_ERROR, "Missing Basic password.");
593 return;
594 }
595
596 *ptr++ = '\0';
597
598 if (!username[0])
599 {
600 /*
601 * Username must not be empty...
602 */
603
604 cupsdLogClient(con, CUPSD_LOG_ERROR, "Empty Basic username.");
605 return;
606 }
607
608 if (!*ptr)
609 {
610 /*
611 * Password must not be empty...
612 */
613
614 cupsdLogClient(con, CUPSD_LOG_ERROR, "Empty Basic password.");
615 return;
616 }
617
618 cupsCopyString(password, ptr, sizeof(password));
619
620 /*
621 * Validate the username and password...
622 */
623
624 if (type == CUPSD_AUTH_BASIC)
625 {
626 #if HAVE_LIBPAM
627 /*
628 * Only use PAM to do authentication. This supports MD5
629 * passwords, among other things...
630 */
631
632 pam_handle_t *pamh; /* PAM authentication handle */
633 int pamerr; /* PAM error code */
634 struct pam_conv pamdata; /* PAM conversation data */
635 cupsd_authdata_t data; /* Authentication data */
636 struct passwd *userinfo; /* User information */
637
638 cupsCopyString(data.username, username, sizeof(data.username));
639 cupsCopyString(data.password, password, sizeof(data.password));
640
641 # ifdef __sun
642 pamdata.conv = (int (*)(int, struct pam_message **,
643 struct pam_response **,
644 void *))pam_func;
645 # else
646 pamdata.conv = pam_func;
647 # endif /* __sun */
648 pamdata.appdata_ptr = &data;
649
650 pamerr = pam_start("cups", username, &pamdata, &pamh);
651 if (pamerr != PAM_SUCCESS)
652 {
653 cupsdLogClient(con, CUPSD_LOG_ERROR, "pam_start() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr));
654 return;
655 }
656
657 # ifdef HAVE_PAM_SET_ITEM
658 # ifdef PAM_RHOST
659 pamerr = pam_set_item(pamh, PAM_RHOST, con->http->hostname);
660 if (pamerr != PAM_SUCCESS)
661 cupsdLogClient(con, CUPSD_LOG_WARN, "pam_set_item(PAM_RHOST) returned %d (%s)", pamerr, pam_strerror(pamh, pamerr));
662 # endif /* PAM_RHOST */
663
664 # ifdef PAM_TTY
665 pamerr = pam_set_item(pamh, PAM_TTY, "cups");
666 if (pamerr != PAM_SUCCESS)
667 cupsdLogClient(con, CUPSD_LOG_WARN, "pam_set_item(PAM_TTY) returned %d (%s)", pamerr, pam_strerror(pamh, pamerr));
668 # endif /* PAM_TTY */
669 # endif /* HAVE_PAM_SET_ITEM */
670
671 pamerr = pam_authenticate(pamh, PAM_SILENT);
672 if (pamerr != PAM_SUCCESS)
673 {
674 cupsdLogClient(con, CUPSD_LOG_ERROR, "pam_authenticate() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr));
675 pam_end(pamh, 0);
676 return;
677 }
678
679 # ifdef HAVE_PAM_SETCRED
680 pamerr = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT);
681 if (pamerr != PAM_SUCCESS)
682 cupsdLogClient(con, CUPSD_LOG_WARN, "pam_setcred() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr));
683 # endif /* HAVE_PAM_SETCRED */
684
685 pamerr = pam_acct_mgmt(pamh, PAM_SILENT);
686 if (pamerr != PAM_SUCCESS)
687 {
688 cupsdLogClient(con, CUPSD_LOG_ERROR, "pam_acct_mgmt() returned %d (%s)", pamerr, pam_strerror(pamh, pamerr));
689 pam_end(pamh, 0);
690 return;
691 }
692
693 pam_end(pamh, PAM_SUCCESS);
694
695 /*
696 * Copy GECOS information, if available, to get the user's real name...
697 */
698
699 if ((userinfo = getpwnam(username)) != NULL && userinfo->pw_gecos)
700 cupsCopyString(con->realname, userinfo->pw_gecos, sizeof(con->realname));
701 #else
702 cupsdLogClient(con, CUPSD_LOG_ERROR, "No authentication support is available.");
703 return;
704 #endif /* HAVE_LIBPAM */
705 }
706
707 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Authorized as \"%s\" using Basic.", username);
708 con->type = type;
709 }
710 else if (!strncmp(authorization, "Bearer ", 7))
711 {
712 // OAuth/OpenID authorization using JWT bearer tokens...
713 cups_jwt_t *jwt; // JWT user information
714 const char *sub, // Subject/user ID
715 *name, // Real name
716 *email; // Email address
717
718 // Skip whitespace after "Bearer"...
719 authorization += 7;
720 while (isspace(*authorization & 255))
721 authorization ++;
722
723 if (!strcmp(authorization, "COOKIE"))
724 authorization = bearer; // Use the cookie value for authorization
725
726 // Decode and validate the JWT...
727 if ((jwt = cupsOAuthGetUserId(OAuthServer, OAuthMetadata, authorization)) == NULL)
728 {
729 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to get user information from bearer token: %s", cupsGetErrorString());
730 cupsCopyString(con->autherror, cupsGetErrorString(), sizeof(con->autherror));
731 return;
732 }
733 else if ((sub = cupsJWTGetClaimString(jwt, CUPS_JWT_SUB)) == NULL)
734 {
735 cupsdLogClient(con, CUPSD_LOG_ERROR, "Missing subject name in user information.");
736 cupsCopyString(con->autherror, "Missing subject name.", sizeof(con->autherror));
737 cupsJWTDelete(jwt);
738 return;
739 }
740
741 // Good JWT, grab information from it and return...
742 con->type = CUPSD_AUTH_BEARER;
743 con->autherror[0] = '\0';
744 con->password[0] = '\0';
745
746 httpSetAuthString(con->http, "Bearer", authorization);
747 cupsCopyString(con->username, sub, sizeof(con->username));
748 if ((name = cupsJWTGetClaimString(jwt, CUPS_JWT_NAME)) != NULL)
749 cupsCopyString(con->realname, name, sizeof(con->realname));
750 if ((email = cupsJWTGetClaimString(jwt, "email")) != NULL)
751 cupsCopyString(con->email, email, sizeof(con->email));
752
753 cupsJWTDelete(jwt);
754
755 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Authorized as \"%s\" (%s <%s>) using OAuth/OpenID.", con->username, con->realname, con->email);
756 return;
757 }
758 #ifdef HAVE_GSSAPI
759 else if (!strncmp(authorization, "Negotiate ", 10))
760 {
761 int len; /* Length of authorization string */
762 gss_ctx_id_t context; /* Authorization context */
763 OM_uint32 major_status, /* Major status code */
764 minor_status; /* Minor status code */
765 gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER,
766 /* Input token from string */
767 output_token = GSS_C_EMPTY_BUFFER;
768 /* Output token for username */
769 gss_name_t client_name; /* Client name */
770
771 # ifdef __APPLE__DISABLED // Remove DISABLED if ever this code is used for macOS installer
772 /*
773 * If the weak-linked GSSAPI/Kerberos library is not present, don't try
774 * to use it...
775 */
776
777 if (&gss_init_sec_context == NULL)
778 {
779 cupsdLogClient(con, CUPSD_LOG_WARN, "GSSAPI/Kerberos authentication failed because the Kerberos framework is not present.");
780 return;
781 }
782 # endif /* __APPLE__DISABLED */
783
784 /*
785 * Find the start of the Kerberos input token...
786 */
787
788 authorization += 9;
789 while (isspace(*authorization & 255))
790 authorization ++;
791
792 if (!*authorization)
793 {
794 cupsdLogClient(con, CUPSD_LOG_DEBUG2, "No authentication data specified.");
795 return;
796 }
797
798 /*
799 * Decode the authorization string to get the input token...
800 */
801
802 len = (int)strlen(authorization) + 0;
803 input_token.value = malloc((size_t)len);
804 input_token.value = httpDecode64_2(input_token.value, &len,
805 authorization);
806 input_token.length = (size_t)len;
807
808 /*
809 * Accept the input token to get the authorization info...
810 */
811
812 context = GSS_C_NO_CONTEXT;
813 client_name = GSS_C_NO_NAME;
814 major_status = gss_accept_sec_context(&minor_status,
815 &context,
816 ServerCreds,
817 &input_token,
818 GSS_C_NO_CHANNEL_BINDINGS,
819 &client_name,
820 NULL,
821 &output_token,
822 NULL,
823 NULL,
824 NULL);
825
826 if (output_token.length > 0)
827 gss_release_buffer(&minor_status, &output_token);
828
829 if (GSS_ERROR(major_status))
830 {
831 cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "[Client %d] Error accepting GSSAPI security context.", con->number);
832
833 if (context != GSS_C_NO_CONTEXT)
834 gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
835 return;
836 }
837
838 con->have_gss = 1;
839
840 /*
841 * Get the username associated with the client's credentials...
842 */
843
844 if (major_status == GSS_S_CONTINUE_NEEDED)
845 cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "[Client %d] Credentials not complete.", con->number);
846 else if (major_status == GSS_S_COMPLETE)
847 {
848 major_status = gss_display_name(&minor_status, client_name,
849 &output_token, NULL);
850
851 if (GSS_ERROR(major_status))
852 {
853 cupsdLogGSSMessage(CUPSD_LOG_DEBUG, major_status, minor_status, "[Client %d] Error getting username.", con->number);
854 gss_release_name(&minor_status, &client_name);
855 gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
856 return;
857 }
858
859 cupsCopyString(username, output_token.value, sizeof(username));
860
861 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Authorized as \"%s\" using Negotiate.", username);
862
863 gss_release_name(&minor_status, &client_name);
864 gss_release_buffer(&minor_status, &output_token);
865
866 con->type = CUPSD_AUTH_NEGOTIATE;
867 }
868
869 gss_delete_sec_context(&minor_status, &context, GSS_C_NO_BUFFER);
870
871 # if defined(SO_PEERCRED) && defined(AF_LOCAL)
872 /*
873 * Get the client's UID if we are printing locally - that allows a backend
874 * to run as the correct user to get Kerberos credentials of its own.
875 */
876
877 if (httpAddrFamily(con->http->hostaddr) == AF_LOCAL)
878 {
879 cupsd_ucred_t peercred; /* Peer credentials */
880 socklen_t peersize; /* Size of peer credentials */
881
882 peersize = sizeof(peercred);
883
884 # ifdef __APPLE__
885 if (getsockopt(httpGetFd(con->http), 0, LOCAL_PEERCRED, &peercred, &peersize))
886 # else
887 if (getsockopt(httpGetFd(con->http), SOL_SOCKET, SO_PEERCRED, &peercred,
888 &peersize))
889 # endif /* __APPLE__ */
890 {
891 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to get peer credentials - %s", strerror(errno));
892 }
893 else
894 {
895 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Using credentials for UID %d.", CUPSD_UCRED_UID(peercred));
896 con->gss_uid = CUPSD_UCRED_UID(peercred);
897 }
898 }
899 # endif /* SO_PEERCRED && AF_LOCAL */
900 }
901 #endif /* HAVE_GSSAPI */
902 else
903 {
904 char scheme[256]; /* Auth scheme... */
905
906
907 if (sscanf(authorization, "%255s", scheme) != 1)
908 cupsCopyString(scheme, "UNKNOWN", sizeof(scheme));
909
910 cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad authentication data \"%s ...\".", scheme);
911 return;
912 }
913
914 /*
915 * If we get here, then we were able to validate the username and
916 * password - copy the validated username and password to the client
917 * data and return...
918 */
919
920 cupsCopyString(con->username, username, sizeof(con->username));
921
922 if (!con->realname[0])
923 cupsCopyString(con->realname, username, sizeof(con->realname));
924
925 cupsCopyString(con->password, password, sizeof(con->password));
926 }
927
928
929 /*
930 * 'cupsdCheckAccess()' - Check whether the given address is allowed to
931 * access a location.
932 */
933
934 int /* O - 1 if allowed, 0 otherwise */
935 cupsdCheckAccess(
936 unsigned ip[4], /* I - Client address */
937 const char *name, /* I - Client hostname */
938 size_t namelen, /* I - Length of hostname */
939 cupsd_location_t *loc) /* I - Location to check */
940 {
941 int allow; /* 1 if allowed, 0 otherwise */
942
943
944 if (!_cups_strcasecmp(name, "localhost"))
945 {
946 /*
947 * Access from localhost (127.0.0.1 or ::1) is always allowed...
948 */
949
950 return (1);
951 }
952 else
953 {
954 /*
955 * Do authorization checks on the domain/address...
956 */
957
958 switch (loc->order_type)
959 {
960 default :
961 allow = 0; /* anti-compiler-warning-code */
962 break;
963
964 case CUPSD_AUTH_ALLOW : /* Order Deny,Allow */
965 allow = 1;
966
967 if (cupsdCheckAuth(ip, name, namelen, loc->deny))
968 allow = 0;
969
970 if (cupsdCheckAuth(ip, name, namelen, loc->allow))
971 allow = 1;
972 break;
973
974 case CUPSD_AUTH_DENY : /* Order Allow,Deny */
975 allow = 0;
976
977 if (cupsdCheckAuth(ip, name, namelen, loc->allow))
978 allow = 1;
979
980 if (cupsdCheckAuth(ip, name, namelen, loc->deny))
981 allow = 0;
982 break;
983 }
984 }
985
986 return (allow);
987 }
988
989
990 /*
991 * 'cupsdCheckAuth()' - Check authorization masks.
992 */
993
994 int /* O - 1 if mask matches, 0 otherwise */
995 cupsdCheckAuth(unsigned ip[4], /* I - Client address */
996 const char *name, /* I - Client hostname */
997 size_t name_len, /* I - Length of hostname */
998 cups_array_t *masks) /* I - Masks */
999 {
1000 int i; /* Looping var */
1001 cupsd_authmask_t *mask; /* Current mask */
1002 cupsd_netif_t *iface; /* Network interface */
1003 unsigned netip4; /* IPv4 network address */
1004 #ifdef AF_INET6
1005 unsigned netip6[4]; /* IPv6 network address */
1006 #endif /* AF_INET6 */
1007
1008
1009 for (mask = (cupsd_authmask_t *)cupsArrayFirst(masks);
1010 mask;
1011 mask = (cupsd_authmask_t *)cupsArrayNext(masks))
1012 {
1013 switch (mask->type)
1014 {
1015 case CUPSD_AUTH_INTERFACE :
1016 /*
1017 * Check for a match with a network interface...
1018 */
1019
1020 netip4 = htonl(ip[3]);
1021
1022 #ifdef AF_INET6
1023 netip6[0] = htonl(ip[0]);
1024 netip6[1] = htonl(ip[1]);
1025 netip6[2] = htonl(ip[2]);
1026 netip6[3] = htonl(ip[3]);
1027 #endif /* AF_INET6 */
1028
1029 cupsdNetIFUpdate();
1030
1031 if (!strcmp(mask->mask.name.name, "*"))
1032 {
1033 #ifdef __APPLE__
1034 /*
1035 * Allow Back-to-My-Mac addresses...
1036 */
1037
1038 if ((ip[0] & 0xff000000) == 0xfd000000)
1039 return (1);
1040 #endif /* __APPLE__ */
1041
1042 /*
1043 * Check against all local interfaces...
1044 */
1045
1046 for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
1047 iface;
1048 iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
1049 {
1050 /*
1051 * Only check local interfaces...
1052 */
1053
1054 if (!iface->is_local)
1055 continue;
1056
1057 if (iface->address.addr.sa_family == AF_INET)
1058 {
1059 /*
1060 * Check IPv4 address...
1061 */
1062
1063 if ((netip4 & iface->mask.ipv4.sin_addr.s_addr) ==
1064 (iface->address.ipv4.sin_addr.s_addr &
1065 iface->mask.ipv4.sin_addr.s_addr))
1066 return (1);
1067 }
1068 #ifdef AF_INET6
1069 else
1070 {
1071 /*
1072 * Check IPv6 address...
1073 */
1074
1075 for (i = 0; i < 4; i ++)
1076 if ((netip6[i] & iface->mask.ipv6.sin6_addr.s6_addr32[i]) !=
1077 (iface->address.ipv6.sin6_addr.s6_addr32[i] &
1078 iface->mask.ipv6.sin6_addr.s6_addr32[i]))
1079 break;
1080
1081 if (i == 4)
1082 return (1);
1083 }
1084 #endif /* AF_INET6 */
1085 }
1086 }
1087 else
1088 {
1089 /*
1090 * Check the named interface...
1091 */
1092
1093 for (iface = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
1094 iface;
1095 iface = (cupsd_netif_t *)cupsArrayNext(NetIFList))
1096 {
1097 if (strcmp(mask->mask.name.name, iface->name))
1098 continue;
1099
1100 if (iface->address.addr.sa_family == AF_INET)
1101 {
1102 /*
1103 * Check IPv4 address...
1104 */
1105
1106 if ((netip4 & iface->mask.ipv4.sin_addr.s_addr) ==
1107 (iface->address.ipv4.sin_addr.s_addr &
1108 iface->mask.ipv4.sin_addr.s_addr))
1109 return (1);
1110 }
1111 #ifdef AF_INET6
1112 else
1113 {
1114 /*
1115 * Check IPv6 address...
1116 */
1117
1118 for (i = 0; i < 4; i ++)
1119 if ((netip6[i] & iface->mask.ipv6.sin6_addr.s6_addr32[i]) !=
1120 (iface->address.ipv6.sin6_addr.s6_addr32[i] &
1121 iface->mask.ipv6.sin6_addr.s6_addr32[i]))
1122 break;
1123
1124 if (i == 4)
1125 return (1);
1126 }
1127 #endif /* AF_INET6 */
1128 }
1129 }
1130 break;
1131
1132 case CUPSD_AUTH_NAME :
1133 /*
1134 * Check for exact name match...
1135 */
1136
1137 if (!_cups_strcasecmp(name, mask->mask.name.name))
1138 return (1);
1139
1140 /*
1141 * Check for domain match...
1142 */
1143
1144 if (name_len >= mask->mask.name.length &&
1145 mask->mask.name.name[0] == '.' &&
1146 !_cups_strcasecmp(name + name_len - mask->mask.name.length,
1147 mask->mask.name.name))
1148 return (1);
1149 break;
1150
1151 case CUPSD_AUTH_IP :
1152 /*
1153 * Check for IP/network address match...
1154 */
1155
1156 for (i = 0; i < 4; i ++)
1157 if ((ip[i] & mask->mask.ip.netmask[i]) !=
1158 mask->mask.ip.address[i])
1159 break;
1160
1161 if (i == 4)
1162 return (1);
1163 break;
1164 }
1165 }
1166
1167 return (0);
1168 }
1169
1170
1171 /*
1172 * 'cupsdCheckGroup()' - Check for a user's group membership.
1173 */
1174
1175 int /* O - 1 if user is a member, 0 otherwise */
1176 cupsdCheckGroup(
1177 const char *username, /* I - User name */
1178 struct passwd *user, /* I - System user info */
1179 const char *groupname) /* I - Group name */
1180 {
1181 int i; /* Looping var */
1182 struct group *group; /* Group info */
1183 gid_t groupid; /* ID of named group */
1184 #ifdef HAVE_MBR_UID_TO_UUID
1185 uuid_t useruuid, /* UUID for username */
1186 groupuuid; /* UUID for groupname */
1187 int is_member; /* True if user is a member of group */
1188 #endif /* HAVE_MBR_UID_TO_UUID */
1189
1190
1191 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckGroup(username=\"%s\", user=%p, groupname=\"%s\")", username, (void *)user, groupname);
1192
1193 /*
1194 * Validate input...
1195 */
1196
1197 if (!username || !groupname)
1198 return (0);
1199
1200 /*
1201 * Check to see if the user is a member of the named group...
1202 */
1203
1204 group = getgrnam(groupname);
1205 endgrent();
1206
1207 if (group != NULL)
1208 {
1209 /*
1210 * Group exists, check it...
1211 */
1212
1213 groupid = group->gr_gid;
1214
1215 for (i = 0; group->gr_mem[i]; i ++)
1216 {
1217 /*
1218 * User appears in the group membership...
1219 */
1220
1221 if (!_cups_strcasecmp(username, group->gr_mem[i]))
1222 return (1);
1223 }
1224
1225 #ifdef HAVE_GETGROUPLIST
1226 /*
1227 * If the user isn't in the group membership list, try the results from
1228 * getgrouplist() which is supposed to return the full list of groups a user
1229 * belongs to...
1230 */
1231
1232 if (user)
1233 {
1234 int ngroups; /* Number of groups */
1235 # ifdef __APPLE__
1236 int groups[2048]; /* Groups that user belongs to */
1237 # else
1238 gid_t groups[2048]; /* Groups that user belongs to */
1239 # endif /* __APPLE__ */
1240
1241 ngroups = (int)(sizeof(groups) / sizeof(groups[0]));
1242 # ifdef __APPLE__
1243 getgrouplist(username, (int)user->pw_gid, groups, &ngroups);
1244 # else
1245 getgrouplist(username, user->pw_gid, groups, &ngroups);
1246 #endif /* __APPLE__ */
1247
1248 for (i = 0; i < ngroups; i ++)
1249 if ((int)groupid == (int)groups[i])
1250 return (1);
1251 }
1252 #endif /* HAVE_GETGROUPLIST */
1253 }
1254 else
1255 groupid = (gid_t)-1;
1256
1257 /*
1258 * Group doesn't exist or user not in group list, check the group ID
1259 * against the user's group ID...
1260 */
1261
1262 if (user && groupid == user->pw_gid)
1263 return (1);
1264
1265 #ifdef HAVE_MBR_UID_TO_UUID
1266 /*
1267 * Check group membership through macOS membership API...
1268 */
1269
1270 if (user && !mbr_uid_to_uuid(user->pw_uid, useruuid))
1271 {
1272 if (groupid != (gid_t)-1)
1273 {
1274 /*
1275 * Map group name to UUID and check membership...
1276 */
1277
1278 if (!mbr_gid_to_uuid(groupid, groupuuid))
1279 if (!mbr_check_membership(useruuid, groupuuid, &is_member))
1280 if (is_member)
1281 return (1);
1282 }
1283 else if (groupname[0] == '#')
1284 {
1285 /*
1286 * Use UUID directly and check for equality (user UUID) and
1287 * membership (group UUID)...
1288 */
1289
1290 if (!uuid_parse((char *)groupname + 1, groupuuid))
1291 {
1292 if (!uuid_compare(useruuid, groupuuid))
1293 return (1);
1294 else if (!mbr_check_membership(useruuid, groupuuid, &is_member))
1295 if (is_member)
1296 return (1);
1297 }
1298
1299 return (0);
1300 }
1301 }
1302 else if (groupname[0] == '#')
1303 return (0);
1304 #endif /* HAVE_MBR_UID_TO_UUID */
1305
1306 /*
1307 * If we get this far, then the user isn't part of the named group...
1308 */
1309
1310 return (0);
1311 }
1312
1313
1314 /*
1315 * 'cupsdCopyLocation()' - Make a copy of a location...
1316 */
1317
1318 cupsd_location_t * /* O - New location */
1319 cupsdCopyLocation(
1320 cupsd_location_t *loc) /* I - Original location */
1321 {
1322 cupsd_location_t *temp; /* New location */
1323
1324
1325 /*
1326 * Make a copy of the original location...
1327 */
1328
1329 if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL)
1330 return (NULL);
1331
1332 /*
1333 * Copy the information from the original location to the new one.
1334 */
1335
1336 if (!loc)
1337 return (temp);
1338
1339 if (loc->location)
1340 temp->location = _cupsStrAlloc(loc->location);
1341
1342 temp->length = loc->length;
1343 temp->limit = loc->limit;
1344 temp->order_type = loc->order_type;
1345 temp->type = loc->type;
1346 temp->level = loc->level;
1347 temp->satisfy = loc->satisfy;
1348 temp->encryption = loc->encryption;
1349
1350 if (loc->names)
1351 {
1352 if ((temp->names = cupsArrayDup(loc->names)) == NULL)
1353 {
1354 cupsdLogMessage(CUPSD_LOG_ERROR,
1355 "Unable to allocate memory for %d names: %s",
1356 cupsArrayCount(loc->names), strerror(errno));
1357
1358 cupsdFreeLocation(temp, NULL);
1359 return (NULL);
1360 }
1361 }
1362
1363 if (loc->allow)
1364 {
1365 /*
1366 * Copy allow rules...
1367 */
1368
1369 if ((temp->allow = cupsArrayDup(loc->allow)) == NULL)
1370 {
1371 cupsdLogMessage(CUPSD_LOG_ERROR,
1372 "Unable to allocate memory for %d allow rules: %s",
1373 cupsArrayCount(loc->allow), strerror(errno));
1374 cupsdFreeLocation(temp, NULL);
1375 return (NULL);
1376 }
1377 }
1378
1379 if (loc->deny)
1380 {
1381 /*
1382 * Copy deny rules...
1383 */
1384
1385 if ((temp->deny = cupsArrayDup(loc->deny)) == NULL)
1386 {
1387 cupsdLogMessage(CUPSD_LOG_ERROR,
1388 "Unable to allocate memory for %d deny rules: %s",
1389 cupsArrayCount(loc->deny), strerror(errno));
1390 cupsdFreeLocation(temp, NULL);
1391 return (NULL);
1392 }
1393 }
1394
1395 return (temp);
1396 }
1397
1398
1399 /*
1400 * 'cupsdDeleteAllLocations()' - Free all memory used for location authorization.
1401 */
1402
1403 void
1404 cupsdDeleteAllLocations(void)
1405 {
1406 /*
1407 * Free the location array, which will free all of the locations...
1408 */
1409
1410 cupsArrayDelete(Locations);
1411 Locations = NULL;
1412 }
1413
1414
1415 /*
1416 * 'cupsdFindBest()' - Find the location entry that best matches the resource.
1417 */
1418
1419 cupsd_location_t * /* O - Location that matches */
1420 cupsdFindBest(const char *path, /* I - Resource path */
1421 http_state_t state) /* I - HTTP state/request */
1422 {
1423 char uri[HTTP_MAX_URI],
1424 /* URI in request... */
1425 *uriptr; /* Pointer into URI */
1426 cupsd_location_t *loc, /* Current location */
1427 *best; /* Best match for location so far */
1428 size_t bestlen; /* Length of best match */
1429 int limit; /* Limit field */
1430 static const int limits[] = /* Map http_status_t to CUPSD_AUTH_LIMIT_xyz */
1431 {
1432 CUPSD_AUTH_LIMIT_ALL,
1433 CUPSD_AUTH_LIMIT_OPTIONS,
1434 CUPSD_AUTH_LIMIT_GET,
1435 CUPSD_AUTH_LIMIT_GET,
1436 CUPSD_AUTH_LIMIT_HEAD,
1437 CUPSD_AUTH_LIMIT_POST,
1438 CUPSD_AUTH_LIMIT_POST,
1439 CUPSD_AUTH_LIMIT_POST,
1440 CUPSD_AUTH_LIMIT_PUT,
1441 CUPSD_AUTH_LIMIT_PUT,
1442 CUPSD_AUTH_LIMIT_DELETE,
1443 CUPSD_AUTH_LIMIT_TRACE,
1444 CUPSD_AUTH_LIMIT_ALL,
1445 CUPSD_AUTH_LIMIT_ALL,
1446 CUPSD_AUTH_LIMIT_ALL,
1447 CUPSD_AUTH_LIMIT_ALL
1448 };
1449
1450
1451 /*
1452 * First copy the connection URI to a local string so we have drop
1453 * any .ppd extension from the pathname in /printers or /classes
1454 * URIs...
1455 */
1456
1457 cupsCopyString(uri, path, sizeof(uri));
1458
1459 if ((uriptr = strchr(uri, '?')) != NULL)
1460 *uriptr = '\0'; /* Drop trailing query string */
1461
1462 if ((uriptr = uri + strlen(uri) - 1) > uri && *uriptr == '/')
1463 *uriptr = '\0'; /* Remove trailing '/' */
1464
1465 if (!strncmp(uri, "/printers/", 10) ||
1466 !strncmp(uri, "/classes/", 9))
1467 {
1468 /*
1469 * Check if the URI has .ppd on the end...
1470 */
1471
1472 uriptr = uri + strlen(uri) - 4; /* len > 4 if we get here... */
1473
1474 if (!strcmp(uriptr, ".ppd"))
1475 *uriptr = '\0';
1476 }
1477
1478 /*
1479 * Loop through the list of locations to find a match...
1480 */
1481
1482 limit = limits[state];
1483 best = NULL;
1484 bestlen = 0;
1485
1486 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: uri=\"%s\", limit=%x...", uri, limit);
1487
1488
1489 for (loc = (cupsd_location_t *)cupsArrayFirst(Locations);
1490 loc;
1491 loc = (cupsd_location_t *)cupsArrayNext(Locations))
1492 {
1493 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: Location %s(%d) Limit %x", loc->location ? loc->location : "(null)", (int)loc->length, loc->limit);
1494
1495 if (!strncmp(uri, "/printers/", 10) || !strncmp(uri, "/classes/", 9))
1496 {
1497 /*
1498 * Use case-insensitive comparison for queue names...
1499 */
1500
1501 if (loc->length > bestlen && loc->location &&
1502 !_cups_strncasecmp(uri, loc->location, loc->length) &&
1503 loc->location[0] == '/' &&
1504 (limit & loc->limit) != 0)
1505 {
1506 best = loc;
1507 bestlen = loc->length;
1508 }
1509 }
1510 else
1511 {
1512 /*
1513 * Use case-sensitive comparison for other URIs...
1514 */
1515
1516 if (loc->length > bestlen && loc->location &&
1517 !strncmp(uri, loc->location, loc->length) &&
1518 loc->location[0] == '/' &&
1519 (limit & loc->limit) != 0)
1520 {
1521 best = loc;
1522 bestlen = loc->length;
1523 }
1524 }
1525 }
1526
1527 /*
1528 * Return the match, if any...
1529 */
1530
1531 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindBest: best=%s", best ? best->location : "NONE");
1532
1533 return (best);
1534 }
1535
1536
1537 /*
1538 * 'cupsdFindLocation()' - Find the named location.
1539 */
1540
1541 cupsd_location_t * /* O - Location that matches */
1542 cupsdFindLocation(const char *location) /* I - Connection */
1543 {
1544 cupsd_location_t key; /* Search key */
1545
1546
1547 key.location = (char *)location;
1548
1549 return ((cupsd_location_t *)cupsArrayFind(Locations, &key));
1550 }
1551
1552
1553 /*
1554 * 'cupsdFindOAuthGroup()' - Find an OAuth group.
1555 */
1556
1557 cupsd_ogroup_t * /* O - Group or `NULL` */
1558 cupsdFindOAuthGroup(const char *name) /* I - Group name */
1559 {
1560 cupsd_ogroup_t key, /* Search key */
1561 *og; /* Matching group */
1562 struct stat fileinfo; /* Group file information */
1563
1564
1565 key.name = (char *)name;
1566 if ((og = (cupsd_ogroup_t *)cupsArrayFind(OAuthGroups, &key)) != NULL)
1567 {
1568 /*
1569 * See if we need to reload the group file...
1570 */
1571
1572 if (!stat(og->filename, &fileinfo) && (fileinfo.st_size != og->fileinfo.st_size || fileinfo.st_mtime > og->fileinfo.st_mtime))
1573 load_ogroup(og, &fileinfo);
1574 }
1575
1576 return (og);
1577 }
1578
1579
1580 /*
1581 * 'cupsdFreeLocation()' - Free all memory used by a location.
1582 */
1583
1584 void cupsdFreeLocation(cupsd_location_t *loc, /* I - Location to free */
1585 void *data) /* Unused */
1586 {
1587 (void)data;
1588 cupsArrayDelete(loc->names);
1589 cupsArrayDelete(loc->allow);
1590 cupsArrayDelete(loc->deny);
1591
1592 _cupsStrFree(loc->location);
1593 free(loc);
1594 }
1595
1596
1597 /*
1598 * 'cupsdIsAuthorized()' - Check to see if the user is authorized...
1599 */
1600
1601 http_status_t /* O - HTTP_OK if authorized or error code */
1602 cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */
1603 const char *owner)/* I - Owner of object */
1604 {
1605 int i, /* Looping vars */
1606 auth, /* Authorization status */
1607 type; /* Type of authentication */
1608 http_addr_t *hostaddr = httpGetAddress(con->http);
1609 /* Client address */
1610 const char *hostname = httpGetHostname(con->http, NULL, 0);
1611 /* Client hostname */
1612 unsigned address[4]; /* Authorization address */
1613 cupsd_location_t *best; /* Best match for location so far */
1614 size_t hostlen; /* Length of hostname */
1615 char *name, /* Current username */
1616 username[256], /* Username to authorize */
1617 ownername[256], /* Owner name to authorize */
1618 *ptr; /* Pointer into username */
1619 struct passwd *pw; /* User password data */
1620 static const char * const levels[] = /* Auth levels */
1621 {
1622 "ANON",
1623 "USER",
1624 "GROUP"
1625 };
1626 static const char * const types[] = /* Auth types */
1627 {
1628 "None",
1629 "Basic",
1630 "Bearer",
1631 "Negotiate"
1632 };
1633
1634
1635 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", con->uri, (void *)con->best, con->best ? con->best->location ? con->best->location : "(null)" : "");
1636 if (owner)
1637 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: owner=\"%s\"", owner);
1638
1639 /*
1640 * If there is no "best" authentication rule for this request, then
1641 * access is allowed from the local system and denied from other
1642 * addresses...
1643 */
1644
1645 if (!con->best)
1646 {
1647 if (httpAddrLocalhost(httpGetAddress(con->http)) ||
1648 !strcmp(hostname, ServerName) ||
1649 cupsArrayFind(ServerAlias, (void *)hostname))
1650 return (HTTP_STATUS_OK);
1651 else
1652 return (HTTP_STATUS_FORBIDDEN);
1653 }
1654
1655 best = con->best;
1656
1657 if ((type = best->type) == CUPSD_AUTH_DEFAULT)
1658 type = cupsdDefaultAuthType();
1659
1660 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: level=CUPSD_AUTH_%s, type=%s, satisfy=CUPSD_AUTH_SATISFY_%s, num_names=%d", levels[best->level], types[type], best->satisfy ? "ANY" : "ALL", cupsArrayCount(best->names));
1661
1662 if (best->limit == CUPSD_AUTH_LIMIT_IPP)
1663 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: op=%x(%s)", best->op, ippOpString(best->op));
1664
1665 /*
1666 * Check host/ip-based accesses...
1667 */
1668
1669 #ifdef AF_INET6
1670 if (httpAddrFamily(hostaddr) == AF_INET6)
1671 {
1672 /*
1673 * Copy IPv6 address...
1674 */
1675
1676 address[0] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[0]);
1677 address[1] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[1]);
1678 address[2] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[2]);
1679 address[3] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[3]);
1680 }
1681 else
1682 #endif /* AF_INET6 */
1683 if (con->http->hostaddr->addr.sa_family == AF_INET)
1684 {
1685 /*
1686 * Copy IPv4 address...
1687 */
1688
1689 address[0] = 0;
1690 address[1] = 0;
1691 address[2] = 0;
1692 address[3] = ntohl(hostaddr->ipv4.sin_addr.s_addr);
1693 }
1694 else
1695 memset(address, 0, sizeof(address));
1696
1697 hostlen = strlen(hostname);
1698
1699 auth = cupsdCheckAccess(address, hostname, hostlen, best)
1700 ? CUPSD_AUTH_ALLOW : CUPSD_AUTH_DENY;
1701
1702 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: auth=CUPSD_AUTH_%s...", auth ? "DENY" : "ALLOW");
1703
1704 if (auth == CUPSD_AUTH_DENY && best->satisfy == CUPSD_AUTH_SATISFY_ALL)
1705 return (HTTP_STATUS_FORBIDDEN);
1706
1707 /*
1708 * See if encryption is required...
1709 */
1710
1711 if ((best->encryption >= HTTP_ENCRYPTION_REQUIRED && !con->http->tls &&
1712 _cups_strcasecmp(hostname, "localhost") &&
1713 !httpAddrLocalhost(hostaddr) &&
1714 best->satisfy == CUPSD_AUTH_SATISFY_ALL) &&
1715 !(type == CUPSD_AUTH_NEGOTIATE ||
1716 (type == CUPSD_AUTH_NONE &&
1717 cupsdDefaultAuthType() == CUPSD_AUTH_NEGOTIATE)))
1718 {
1719 cupsdLogMessage(CUPSD_LOG_DEBUG,
1720 "cupsdIsAuthorized: Need upgrade to TLS...");
1721 return (HTTP_STATUS_UPGRADE_REQUIRED);
1722 }
1723
1724 /*
1725 * Now see what access level is required...
1726 */
1727
1728 if (best->level == CUPSD_AUTH_ANON || /* Anonymous access - allow it */
1729 (type == CUPSD_AUTH_NONE && cupsArrayCount(best->names) == 0))
1730 return (HTTP_STATUS_OK);
1731
1732 if (!con->username[0] && type == CUPSD_AUTH_NONE &&
1733 best->limit == CUPSD_AUTH_LIMIT_IPP)
1734 {
1735 /*
1736 * Check for unauthenticated username...
1737 */
1738
1739 ipp_attribute_t *attr; /* requesting-user-name attribute */
1740
1741
1742 attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME);
1743 if (attr)
1744 {
1745 cupsdLogMessage(CUPSD_LOG_DEBUG,
1746 "cupsdIsAuthorized: requesting-user-name=\"%s\"",
1747 attr->values[0].string.text);
1748 cupsCopyString(username, attr->values[0].string.text, sizeof(username));
1749 }
1750 else if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
1751 return (HTTP_STATUS_UNAUTHORIZED); /* Non-anonymous needs user/pass */
1752 else
1753 return (HTTP_STATUS_OK); /* unless overridden with Satisfy */
1754 }
1755 else
1756 {
1757 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: username=\"%s\"",
1758 con->username);
1759
1760 #ifdef HAVE_AUTHORIZATION_H
1761 if (!con->username[0] && !con->authref)
1762 #else
1763 if (!con->username[0])
1764 #endif /* HAVE_AUTHORIZATION_H */
1765 {
1766 if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY)
1767 return (HTTP_STATUS_UNAUTHORIZED); /* Non-anonymous needs user/pass */
1768 else
1769 return (HTTP_STATUS_OK); /* unless overridden with Satisfy */
1770 }
1771
1772
1773 if (con->type != type && type != CUPSD_AUTH_NONE &&
1774 #ifdef HAVE_GSSAPI
1775 (type != CUPSD_AUTH_NEGOTIATE || con->gss_uid <= 0) &&
1776 #endif /* HAVE_GSSAPI */
1777 con->type != CUPSD_AUTH_BASIC)
1778 {
1779 cupsdLogMessage(CUPSD_LOG_ERROR, "Authorized using %s, expected %s.",
1780 types[con->type], types[type]);
1781
1782 return (HTTP_STATUS_UNAUTHORIZED);
1783 }
1784
1785 cupsCopyString(username, con->username, sizeof(username));
1786 }
1787
1788 /*
1789 * OK, got a username. See if we need normal user access, or group
1790 * access...
1791 */
1792
1793 /*
1794 * Strip any @domain or @KDC from the username and owner...
1795 */
1796
1797 if (type != CUPSD_AUTH_BEARER && StripUserDomain && (ptr = strchr(username, '@')) != NULL)
1798 *ptr = '\0';
1799
1800 if (owner)
1801 {
1802 cupsCopyString(ownername, owner, sizeof(ownername));
1803
1804 if (type != CUPSD_AUTH_BEARER && StripUserDomain && (ptr = strchr(ownername, '@')) != NULL)
1805 *ptr = '\0';
1806 }
1807 else
1808 ownername[0] = '\0';
1809
1810 if (type == CUPSD_AUTH_BEARER)
1811 {
1812 /*
1813 * Lookup access via OAuth groups...
1814 */
1815
1816 cupsd_ogroup_t *og; // Current OAuth group
1817
1818 if (best->level == CUPSD_AUTH_USER)
1819 {
1820 /*
1821 * If there are no names associated with this location, then any valid user
1822 * is OK...
1823 */
1824
1825 if (cupsArrayCount(best->names) == 0)
1826 return (HTTP_STATUS_OK);
1827
1828 /*
1829 * Otherwise check the user list and return OK if this user is allowed...
1830 */
1831
1832 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership...");
1833
1834 for (name = (char *)cupsArrayFirst(best->names); name; name = (char *)cupsArrayNext(best->names))
1835 {
1836 if (!_cups_strcasecmp(name, "@OWNER") && owner && !_cups_strcasecmp(username, ownername))
1837 {
1838 // User is owner...
1839 return (HTTP_STATUS_OK);
1840 }
1841 else if (name[0] == '@')
1842 {
1843 // Check OAuth group membership...
1844 if ((og = cupsdFindOAuthGroup(name + 1)) == NULL)
1845 {
1846 // Group not defined...
1847 cupsdLogMessage(CUPSD_LOG_ERROR, "Authorization policy requires undefined OAuth group \"%s\", ignoring.", name + 1);
1848 }
1849 else if (cupsArrayFind(og->members, username) || (con->email[0] && cupsArrayFind(og->members, con->email)))
1850 {
1851 // User is in group...
1852 return (HTTP_STATUS_OK);
1853 }
1854 }
1855 else if (!_cups_strcasecmp(username, name) || (con->email[0] && !_cups_strcasecmp(con->email, name)))
1856 {
1857 return (HTTP_STATUS_OK);
1858 }
1859 }
1860 }
1861 else
1862 {
1863 /*
1864 * Check to see if this user is in any of the named groups...
1865 */
1866
1867 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership...");
1868
1869 /*
1870 * Check to see if this user is in any of the named groups...
1871 */
1872
1873 for (name = (char *)cupsArrayFirst(best->names); name; name = (char *)cupsArrayNext(best->names))
1874 {
1875 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership.", name);
1876
1877 if ((og = cupsdFindOAuthGroup(name)) == NULL)
1878 {
1879 // Group not defined...
1880 cupsdLogMessage(CUPSD_LOG_ERROR, "Authorization policy requires undefined OAuth group \"%s\", ignoring.", name + 1);
1881 }
1882 else if (cupsArrayFind(og->members, username) || (con->email[0] && cupsArrayFind(og->members, con->email)))
1883 {
1884 // User is in group...
1885 return (HTTP_STATUS_OK);
1886 }
1887 }
1888 }
1889 }
1890 else
1891 {
1892 /*
1893 * Get the (local) user info...
1894 */
1895
1896 if (username[0])
1897 {
1898 pw = getpwnam(username);
1899 endpwent();
1900 }
1901 else
1902 pw = NULL;
1903
1904 /*
1905 * For matching user and group memberships below we will first go
1906 * through all names except @SYSTEM to authorize the task as
1907 * non-administrative, like printing or deleting one's own job, if this
1908 * fails we will check whether we can authorize via the special name
1909 * @SYSTEM, as an administrative task, like creating a print queue or
1910 * deleting someone else's job.
1911 *
1912 * Note that tasks are considered as administrative by the policies
1913 * in cupsd.conf, when they require the user or group @SYSTEM.
1914 * We do this separation because if the client is a Snap connecting via
1915 * domain socket, we need to additionally check whether it plugs to us
1916 * through the "cups-control" interface which allows administration and
1917 * not through the "cups" interface which allows only printing.
1918 */
1919
1920 if (best->level == CUPSD_AUTH_USER)
1921 {
1922 /*
1923 * If there are no names associated with this location, then
1924 * any valid user is OK...
1925 */
1926
1927 if (cupsArrayCount(best->names) == 0)
1928 return (HTTP_STATUS_OK);
1929
1930 /*
1931 * Otherwise check the user list and return OK if this user is
1932 * allowed...
1933 */
1934
1935 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership...");
1936
1937 #ifdef HAVE_AUTHORIZATION_H
1938 /*
1939 * If an authorization reference was supplied it must match a right name...
1940 */
1941
1942 if (con->authref)
1943 {
1944 for (name = (char *)cupsArrayFirst(best->names);
1945 name;
1946 name = (char *)cupsArrayNext(best->names))
1947 {
1948 if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9))
1949 return (HTTP_STATUS_OK);
1950 }
1951
1952 for (name = (char *)cupsArrayFirst(best->names);
1953 name;
1954 name = (char *)cupsArrayNext(best->names))
1955 {
1956 if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey &&
1957 check_authref(con, SystemGroupAuthKey))
1958 return (HTTP_STATUS_OK);
1959 }
1960
1961 return (HTTP_STATUS_FORBIDDEN);
1962 }
1963 #endif /* HAVE_AUTHORIZATION_H */
1964
1965 for (name = (char *)cupsArrayFirst(best->names);
1966 name;
1967 name = (char *)cupsArrayNext(best->names))
1968 {
1969 if (!_cups_strcasecmp(name, "@OWNER") && owner &&
1970 !_cups_strcasecmp(username, ownername))
1971 return (HTTP_STATUS_OK);
1972 else if (!_cups_strcasecmp(name, "@SYSTEM"))
1973 {
1974 /* Do @SYSTEM later, when every other entry fails */
1975 continue;
1976 }
1977 else if (name[0] == '@')
1978 {
1979 if (cupsdCheckGroup(username, pw, name + 1))
1980 return (HTTP_STATUS_OK);
1981 }
1982 else if (!_cups_strcasecmp(username, name))
1983 return (HTTP_STATUS_OK);
1984 }
1985
1986 for (name = (char *)cupsArrayFirst(best->names);
1987 name;
1988 name = (char *)cupsArrayNext(best->names))
1989 {
1990 if (!_cups_strcasecmp(name, "@SYSTEM"))
1991 {
1992 for (i = 0; i < NumSystemGroups; i ++)
1993 if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_access(con))
1994 return (HTTP_STATUS_OK);
1995 }
1996 }
1997 }
1998 else
1999 {
2000 /*
2001 * Check to see if this user is in any of the named groups...
2002 */
2003
2004 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership...");
2005
2006 /*
2007 * Check to see if this user is in any of the named groups...
2008 */
2009
2010 for (name = (char *)cupsArrayFirst(best->names);
2011 name;
2012 name = (char *)cupsArrayNext(best->names))
2013 {
2014 if (!_cups_strcasecmp(name, "@SYSTEM"))
2015 {
2016 /* Do @SYSTEM later, when every other entry fails */
2017 continue;
2018 }
2019
2020 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
2021
2022 if (cupsdCheckGroup(username, pw, name))
2023 return (HTTP_STATUS_OK);
2024 }
2025
2026 for (name = (char *)cupsArrayFirst(best->names);
2027 name;
2028 name = (char *)cupsArrayNext(best->names))
2029 {
2030 if (!_cups_strcasecmp(name, "@SYSTEM"))
2031 {
2032 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name);
2033
2034 for (i = 0; i < NumSystemGroups; i ++)
2035 if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_access(con))
2036 return (HTTP_STATUS_OK);
2037 }
2038 }
2039 }
2040 }
2041
2042 /*
2043 * The user isn't part of the specified users or groups, so deny access...
2044 */
2045
2046 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: User not in group(s).");
2047
2048 return (con->username[0] ? HTTP_STATUS_FORBIDDEN : HTTP_STATUS_UNAUTHORIZED);
2049 }
2050
2051
2052 /*
2053 * 'cupsdNewLocation()' - Create a new location for authorization.
2054 *
2055 * Note: Still need to call cupsdAddLocation() to add it to the list of global
2056 * locations.
2057 */
2058
2059 cupsd_location_t * /* O - Pointer to new location record */
2060 cupsdNewLocation(const char *location) /* I - Location path */
2061 {
2062 cupsd_location_t *temp; /* New location */
2063
2064
2065 /*
2066 * Try to allocate memory for the new location.
2067 */
2068
2069 if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL)
2070 return (NULL);
2071
2072 /*
2073 * Initialize the record and copy the name over...
2074 */
2075
2076 if ((temp->location = _cupsStrAlloc(location)) == NULL)
2077 {
2078 free(temp);
2079 return (NULL);
2080 }
2081
2082 temp->length = strlen(temp->location);
2083
2084 /*
2085 * Return the new record...
2086 */
2087
2088 return (temp);
2089 }
2090
2091
2092 /*
2093 * 'check_admin_access()' - Verify that the client has administrative access.
2094 */
2095
2096 static int // O - 1 if authorized, 0 otherwise
2097 check_admin_access(cupsd_client_t *con) // I - Client connection
2098 {
2099 (void)con;
2100
2101 #if defined(HAVE_LIBAPPARMOR) && defined(HAVE_LIBSNAPDGLIB)
2102 /*
2103 * If the client accesses locally via domain socket, find out whether it
2104 * is a Snap. Grant access if it is not a Snap, if it is a classic Snap
2105 * or if it is a confined Snap which plugs "cups-control". Otherwise deny
2106 * access.
2107 */
2108
2109 int fd = httpGetFd(con->http);
2110 // Client socket file descriptor
2111 char *context = NULL; // AppArmor profile name of client
2112 SnapdClient *client = NULL; // Data structure of snapd access
2113 GError *error = NULL; // Glib error
2114 int ret = 1; // Return value
2115 # if !CUPS_SNAP
2116 SnapdSnap *snap = NULL; // Data structure of client Snap
2117 # endif // !CUPS_SNAP
2118
2119
2120 # ifdef AF_LOCAL
2121 // Only check domain sockets...
2122 if (httpAddrFamily(con->http->hostaddr) != AF_LOCAL)
2123 return (1);
2124 # endif // AF_LOCAL
2125
2126 # if !CUPS_SNAP
2127 // If AppArmor is not enabled, then we can't identify the client...
2128 if (!aa_is_enabled())
2129 {
2130 cupsdLogClient(con, CUPSD_LOG_DEBUG, "AppArmor not in use.");
2131 return (1);
2132 }
2133 # endif /* !CUPS_SNAP */
2134
2135 // Get the client's AppArmor context using the socket...
2136 if (aa_getpeercon(fd, &context, NULL) < 0)
2137 {
2138 cupsdLogClient(con, CUPSD_LOG_DEBUG, "AppArmor profile could not be retrieved: %s", strerror(errno));
2139 return (1);
2140 }
2141 else
2142 {
2143 cupsdLogClient(con, CUPSD_LOG_DEBUG, "AppArmor profile is '%s'.", context);
2144 }
2145
2146 // Allow access from "cups" snap...
2147 if (!strncmp(context, "snap.cups.", 10))
2148 {
2149 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Client from the CUPS Snap itself - allowed.");
2150 goto done;
2151 }
2152
2153 # if CUPS_SNAP && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC)
2154 /*
2155 * CUPS is snapped, so check whether the client is also snapped. If so,
2156 * determine whether the client snap has a "cups-control" plug which allows
2157 * the application to perform CUPS administrative tasks.
2158 */
2159
2160 const char *cookie; // snapd access cookie
2161 int status = 65535; // Status of client Snap context check
2162 const char *args[] = // snapctl arguments
2163 {
2164 "is-connected",
2165 "--apparmor-label",
2166 NULL,
2167 "cups-control",
2168 NULL
2169 };
2170
2171 // Connect to snapd
2172 if ((client = snapd_client_new()) == NULL)
2173 {
2174 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to connect to snapd.");
2175 ret = 0;
2176 goto done;
2177 }
2178
2179 // snapctl commands are sent over a domain socket
2180 snapd_client_set_socket_path(client, "/run/snapd-snap.socket");
2181
2182 // Take cookie from the environment if available
2183 if ((cookie = g_getenv("SNAP_COOKIE")) == NULL)
2184 {
2185 cookie = "";
2186 cupsdLogClient(con, CUPSD_LOG_WARN, "No SNAP_COOKIE set in the Snap environment.");
2187 }
2188
2189 // Do the client Snap context check...
2190 args[2] = context;
2191
2192 if (!snapd_client_run_snapctl2_sync(client, cookie, (char **)args, NULL, NULL, &status, NULL, &error))
2193 {
2194 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to check snap context: %s", error->message);
2195 ret = 0;
2196 goto done;
2197 }
2198
2199 switch (status)
2200 {
2201 case 0 : // The client is a confined Snap and plugs cups-control
2202 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Snap with cups-control plug - allowed.");
2203 break;
2204 case 1 : // The client is a confined Snap and does not plug cups-control
2205 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Snap without cups-control plug - denied.");
2206 ret = 0;
2207 break;
2208 case 10 : // The client is a classic Snap
2209 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Classic snap - allowed.");
2210 break;
2211 case 11 : // The client is not a Snap
2212 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Not a snap - allowed.");
2213 break;
2214 default : // Unexpected status...
2215 cupsdLogClient(con, CUPSD_LOG_ERROR, "Snap check returned unexpected status %d - denied.", status);
2216 ret = 0;
2217 break;
2218 }
2219
2220 # elif !CUPS_SNAP
2221 /*
2222 * If CUPS is not snapped, check whether the client is snapped and if it has
2223 * the "cups-control" plug.
2224 */
2225
2226 // Is the client a snapped application?
2227 if (strncmp(context, "snap.", 5))
2228 {
2229 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Not a snap - allowed.");
2230 goto done;
2231 }
2232
2233 // Extract the snap name from the context (snap.name.instance)
2234 char *snap_name = strdup(context + 5);// Snap name follows "snap."
2235 char *ptr = strchr(snap_name, '.'); // instance follows the name...
2236 if (!ptr)
2237 {
2238 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Malformed snapd AppArmor profile name '%s' - denied.", context);
2239 free(snap_name);
2240 ret = 0;
2241 goto done;
2242 }
2243
2244 *ptr = '\0';
2245 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Client snap is '%s'.", snap_name);
2246
2247 // Connect to snapd
2248 if ((client = snapd_client_new()) == NULL)
2249 {
2250 cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to connect to snapd.");
2251 free(snap_name);
2252 ret = 0;
2253 goto done;
2254 }
2255
2256 // Check whether the client Snap is under classic confinement
2257 GPtrArray *plugs = NULL; // List of plugs for snap
2258
2259 if ((snap = snapd_client_get_snap_sync(client, snap_name, NULL, &error)) == NULL)
2260 {
2261 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Unable to get client Snap data: %s", error->message);
2262 ret = 0;
2263 }
2264 // Snaps using classic confinement are granted access
2265 else if (snapd_snap_get_confinement(snap) == SNAPD_CONFINEMENT_CLASSIC)
2266 {
2267 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Classic snap - allowed.");
2268 }
2269 // Check whether the client Snap has the cups-control plug
2270 else if (!snapd_client_get_connections2_sync(client, SNAPD_GET_CONNECTIONS_FLAGS_NONE, snap_name, "cups-control", NULL, NULL, &plugs, NULL, NULL, &error))
2271 {
2272 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Unable to get client Snap plugs: %s", error->message);
2273 ret = 0;
2274 }
2275 else if (!plugs || plugs->len <= 0)
2276 {
2277 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Snap without cups-control plug - denied.");
2278 ret = 0;
2279 }
2280 else
2281 {
2282 cupsdLogClient(con, CUPSD_LOG_DEBUG, "Snap with cups-control plug - allowed.");
2283 }
2284
2285 if (plugs)
2286 g_ptr_array_unref(plugs);
2287
2288 free(snap_name);
2289 g_clear_object(&snap);
2290
2291 # endif // CUPS_SNAP
2292
2293 done:
2294
2295 free(context);
2296 g_clear_object(&client);
2297
2298 return (ret);
2299
2300 #else
2301 // No AppArmor/snapd to deal with...
2302 return (1);
2303 #endif // HAVE_LIBAPPARMOR && HAVE_LIBSNAPDGLIB
2304 }
2305
2306
2307 #ifdef HAVE_AUTHORIZATION_H
2308 /*
2309 * 'check_authref()' - Check if an authorization services reference has the
2310 * supplied right.
2311 */
2312
2313 static int /* O - 1 if right is valid, 0 otherwise */
2314 check_authref(cupsd_client_t *con, /* I - Connection */
2315 const char *right) /* I - Right name */
2316 {
2317 OSStatus status; /* OS Status */
2318 AuthorizationItem authright; /* Authorization right */
2319 AuthorizationRights authrights; /* Authorization rights */
2320 AuthorizationFlags authflags; /* Authorization flags */
2321
2322
2323 /*
2324 * Check to see if the user is allowed to perform the task...
2325 */
2326
2327 if (!con->authref)
2328 return (0);
2329
2330 authright.name = right;
2331 authright.valueLength = 0;
2332 authright.value = NULL;
2333 authright.flags = 0;
2334
2335 authrights.count = 1;
2336 authrights.items = &authright;
2337
2338 authflags = kAuthorizationFlagDefaults |
2339 kAuthorizationFlagExtendRights;
2340
2341 if ((status = AuthorizationCopyRights(con->authref, &authrights,
2342 kAuthorizationEmptyEnvironment,
2343 authflags, NULL)) != 0)
2344 {
2345 cupsdLogMessage(CUPSD_LOG_ERROR, "AuthorizationCopyRights(\"%s\") returned %d", authright.name, (int)status);
2346 return (0);
2347 }
2348
2349 cupsdLogMessage(CUPSD_LOG_DEBUG2, "AuthorizationCopyRights(\"%s\") succeeded.", authright.name);
2350
2351 return (1);
2352 }
2353 #endif /* HAVE_AUTHORIZATION_H */
2354
2355
2356 /*
2357 * 'compare_locations()' - Compare two locations.
2358 */
2359
2360 static int /* O - Result of comparison */
2361 compare_locations(cupsd_location_t *a, /* I - First location */
2362 cupsd_location_t *b, /* I - Second location */
2363 void *data) /* I - Callback data (unused) */
2364 {
2365 (void)data;
2366
2367 return (strcmp(b->location, a->location));
2368 }
2369
2370
2371 /*
2372 * 'compare_ogroups()' - Compare two OAuth groups.
2373 */
2374
2375 static int /* O - Result of comparison */
2376 compare_ogroups(cupsd_ogroup_t *a, /* I - First group */
2377 cupsd_ogroup_t *b, /* I - Second group */
2378 void *data) /* I - Callback data (unused) */
2379 {
2380 (void)data;
2381
2382 return (_cups_strcasecmp(a->name, b->name));
2383 }
2384
2385
2386 /*
2387 * 'copy_authmask()' - Copy function for auth masks.
2388 */
2389
2390 static cupsd_authmask_t * /* O - New auth mask */
2391 copy_authmask(cupsd_authmask_t *mask, /* I - Existing auth mask */
2392 void *data) /* I - User data (unused) */
2393 {
2394 cupsd_authmask_t *temp; /* New auth mask */
2395
2396
2397 (void)data;
2398
2399 if ((temp = malloc(sizeof(cupsd_authmask_t))) != NULL)
2400 {
2401 memcpy(temp, mask, sizeof(cupsd_authmask_t));
2402
2403 if (temp->type == CUPSD_AUTH_NAME || temp->type == CUPSD_AUTH_INTERFACE)
2404 {
2405 /*
2406 * Make a copy of the name...
2407 */
2408
2409 if ((temp->mask.name.name = _cupsStrAlloc(temp->mask.name.name)) == NULL)
2410 {
2411 /*
2412 * Failed to make copy...
2413 */
2414
2415 free(temp);
2416 temp = NULL;
2417 }
2418 }
2419 }
2420
2421 return (temp);
2422 }
2423
2424
2425 /*
2426 * 'free_authmask()' - Free function for auth masks.
2427 */
2428
2429 static void
2430 free_authmask(cupsd_authmask_t *mask, /* I - Auth mask to free */
2431 void *data) /* I - Callback data (unused) */
2432 {
2433 (void)data;
2434
2435 if (mask->type == CUPSD_AUTH_NAME || mask->type == CUPSD_AUTH_INTERFACE)
2436 _cupsStrFree(mask->mask.name.name);
2437
2438 free(mask);
2439 }
2440
2441
2442 /*
2443 * 'free_ogroup()' - Free an OAuth group.
2444 */
2445
2446 static void
2447 free_ogroup(cupsd_ogroup_t *og, /* I - OAuth group */
2448 void *data) /* I - Callback data (unused) */
2449 {
2450 (void)data;
2451
2452 free(og->name);
2453 free(og->filename);
2454 cupsArrayDelete(og->members);
2455 free(og);
2456 }
2457
2458
2459 /*
2460 * 'load_ogroup()' - Load an OAuth group file.
2461 */
2462
2463 static int /* O - 1 on success, 0 on failure */
2464 load_ogroup(cupsd_ogroup_t *og, /* I - OAuth group */
2465 struct stat *fileinfo) /* I - File information */
2466 {
2467 cups_file_t *fp; /* File pointer */
2468 char line[1024]; /* Line from file */
2469
2470
2471 /*
2472 * Make sure we have a fresh members array...
2473 */
2474
2475 cupsArrayDelete(og->members);
2476 if ((og->members = cupsArrayNewStrings(/*s*/NULL, /*delim*/'\0')) == NULL)
2477 {
2478 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate members array for OAuth group %s: %s", og->name, strerror(errno));
2479 return (0);
2480 }
2481
2482 /*
2483 * Load the members file...
2484 */
2485
2486 if ((fp = cupsFileOpen(og->filename, "r")) == NULL)
2487 {
2488 cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to open OAuth group %s filename \"%s\": %s", og->name, og->filename, strerror(errno));
2489 return (0);
2490 }
2491
2492 while (cupsFileGets(fp, line, sizeof(line)))
2493 cupsArrayAdd(og->members, line);
2494
2495 cupsFileClose(fp);
2496
2497 og->fileinfo = *fileinfo;
2498
2499 return (1);
2500 }
2501
2502
2503 #if HAVE_LIBPAM
2504 /*
2505 * 'pam_func()' - PAM conversation function.
2506 */
2507
2508 static int /* O - Success or failure */
2509 pam_func(
2510 int num_msg, /* I - Number of messages */
2511 const struct pam_message **msg, /* I - Messages */
2512 struct pam_response **resp, /* O - Responses */
2513 void *appdata_ptr)
2514 /* I - Pointer to connection */
2515 {
2516 int i; /* Looping var */
2517 struct pam_response *replies; /* Replies */
2518 cupsd_authdata_t *data; /* Pointer to auth data */
2519
2520
2521 /*
2522 * Allocate memory for the responses...
2523 */
2524
2525 if ((replies = malloc(sizeof(struct pam_response) * (size_t)num_msg)) == NULL)
2526 return (PAM_CONV_ERR);
2527
2528 /*
2529 * Answer all of the messages...
2530 */
2531
2532 data = (cupsd_authdata_t *)appdata_ptr;
2533
2534 for (i = 0; i < num_msg; i ++)
2535 {
2536 switch (msg[i]->msg_style)
2537 {
2538 case PAM_PROMPT_ECHO_ON:
2539 replies[i].resp_retcode = PAM_SUCCESS;
2540 replies[i].resp = strdup(data->username);
2541 break;
2542
2543 case PAM_PROMPT_ECHO_OFF:
2544 replies[i].resp_retcode = PAM_SUCCESS;
2545 replies[i].resp = strdup(data->password);
2546 break;
2547
2548 case PAM_TEXT_INFO:
2549 replies[i].resp_retcode = PAM_SUCCESS;
2550 replies[i].resp = NULL;
2551 break;
2552
2553 case PAM_ERROR_MSG:
2554 replies[i].resp_retcode = PAM_SUCCESS;
2555 replies[i].resp = NULL;
2556 break;
2557
2558 default:
2559 free(replies);
2560 return (PAM_CONV_ERR);
2561 }
2562 }
2563
2564 /*
2565 * Return the responses back to PAM...
2566 */
2567
2568 *resp = replies;
2569
2570 return (PAM_SUCCESS);
2571 }
2572 #endif /* HAVE_LIBPAM */