1 /*
2 * Policy routines for the CUPS scheduler.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2011, 2014 by Apple Inc.
6 * Copyright © 1997-2006 by Easy Software Products, all rights reserved.
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 #include <pwd.h>
18
19
20 /*
21 * Local functions...
22 */
23
24 static int compare_ops(cupsd_location_t *a, cupsd_location_t *b);
25 static int compare_policies(cupsd_policy_t *a, cupsd_policy_t *b);
26 static void free_policy(cupsd_policy_t *p);
27 static int hash_op(cupsd_location_t *op);
28
29
30 /*
31 * 'cupsdAddPolicy()' - Add a policy to the system.
32 */
33
34 cupsd_policy_t * /* O - Policy */
cupsdAddPolicy(const char * policy)35 cupsdAddPolicy(const char *policy) /* I - Name of policy */
36 {
37 cupsd_policy_t *temp; /* Pointer to policy */
38
39
40 if (!policy)
41 return (NULL);
42
43 if (!Policies)
44 Policies = cupsArrayNew3((cups_array_func_t)compare_policies, NULL,
45 (cups_ahash_func_t)NULL, 0,
46 (cups_acopy_func_t)NULL,
47 (cups_afree_func_t)free_policy);
48
49 if (!Policies)
50 return (NULL);
51
52 if ((temp = calloc(1, sizeof(cupsd_policy_t))) != NULL)
53 {
54 cupsdSetString(&temp->name, policy);
55 cupsArrayAdd(Policies, temp);
56 }
57
58 return (temp);
59 }
60
61
62 /*
63 * 'cupsdAddPolicyOp()' - Add an operation to a policy.
64 */
65
66 cupsd_location_t * /* O - New policy operation */
cupsdAddPolicyOp(cupsd_policy_t * p,cupsd_location_t * po,ipp_op_t op)67 cupsdAddPolicyOp(cupsd_policy_t *p, /* I - Policy */
68 cupsd_location_t *po, /* I - Policy operation to copy */
69 ipp_op_t op) /* I - IPP operation code */
70 {
71 cupsd_location_t *temp; /* New policy operation */
72
73
74 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddPolicyOp(p=%p, po=%p, op=%x(%s))",
75 p, po, op, ippOpString(op));
76
77 if (!p)
78 return (NULL);
79
80 if (!p->ops)
81 p->ops = cupsArrayNew3((cups_array_func_t)compare_ops, NULL,
82 (cups_ahash_func_t)hash_op, 128,
83 (cups_acopy_func_t)NULL,
84 (cups_afree_func_t)cupsdFreeLocation);
85
86 if (!p->ops)
87 return (NULL);
88
89 if ((temp = cupsdCopyLocation(po)) != NULL)
90 {
91 temp->op = op;
92 temp->limit = CUPSD_AUTH_LIMIT_IPP;
93
94 cupsArrayAdd(p->ops, temp);
95 }
96
97 return (temp);
98 }
99
100
101 /*
102 * 'cupsdCheckPolicy()' - Check the IPP operation and username against a policy.
103 */
104
105 http_status_t /* I - 1 if OK, 0 otherwise */
cupsdCheckPolicy(cupsd_policy_t * p,cupsd_client_t * con,const char * owner)106 cupsdCheckPolicy(cupsd_policy_t *p, /* I - Policy */
107 cupsd_client_t *con, /* I - Client connection */
108 const char *owner) /* I - Owner of object */
109 {
110 cupsd_location_t *po; /* Current policy operation */
111
112
113 /*
114 * Range check...
115 */
116
117 if (!p || !con)
118 {
119 cupsdLogMessage(CUPSD_LOG_CRIT, "cupsdCheckPolicy: p=%p, con=%p.", p, con);
120
121 return (HTTP_STATUS_SERVER_ERROR);
122 }
123
124 /*
125 * Find a match for the operation...
126 */
127
128 if ((po = cupsdFindPolicyOp(p, con->request->request.op.operation_id)) == NULL)
129 {
130 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckPolicy: No matching operation, returning 0.");
131 return (HTTP_STATUS_OK);
132 }
133
134 con->best = po;
135
136 /*
137 * Return the status of the check...
138 */
139
140 return (cupsdIsAuthorized(con, owner));
141 }
142
143
144 /*
145 * 'cupsdDeleteAllPolicies()' - Delete all policies in memory.
146 */
147
148 void
cupsdDeleteAllPolicies(void)149 cupsdDeleteAllPolicies(void)
150 {
151 cupsd_printer_t *printer; /* Current printer */
152
153
154 if (!Policies)
155 return;
156
157 /*
158 * First clear the policy pointers for all printers...
159 */
160
161 for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers);
162 printer;
163 printer = (cupsd_printer_t *)cupsArrayNext(Printers))
164 printer->op_policy_ptr = NULL;
165
166 DefaultPolicyPtr = NULL;
167
168 /*
169 * Then free all of the policies...
170 */
171
172 cupsArrayDelete(Policies);
173
174 Policies = NULL;
175 }
176
177
178 /*
179 * 'cupsdFindPolicy()' - Find a named policy.
180 */
181
182 cupsd_policy_t * /* O - Policy */
cupsdFindPolicy(const char * policy)183 cupsdFindPolicy(const char *policy) /* I - Name of policy */
184 {
185 cupsd_policy_t key; /* Search key */
186
187
188 /*
189 * Range check...
190 */
191
192 if (!policy)
193 return (NULL);
194
195 /*
196 * Look it up...
197 */
198
199 key.name = (char *)policy;
200 return ((cupsd_policy_t *)cupsArrayFind(Policies, &key));
201 }
202
203
204 /*
205 * 'cupsdFindPolicyOp()' - Find a policy operation.
206 */
207
208 cupsd_location_t * /* O - Policy operation */
cupsdFindPolicyOp(cupsd_policy_t * p,ipp_op_t op)209 cupsdFindPolicyOp(cupsd_policy_t *p, /* I - Policy */
210 ipp_op_t op) /* I - IPP operation */
211 {
212 cupsd_location_t key, /* Search key... */
213 *po; /* Current policy operation */
214
215
216 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindPolicyOp(p=%p, op=%x(%s))",
217 p, op, ippOpString(op));
218
219 /*
220 * Range check...
221 */
222
223 if (!p)
224 return (NULL);
225
226 /*
227 * Check the operation against the available policies...
228 */
229
230 key.op = op;
231 if ((po = (cupsd_location_t *)cupsArrayFind(p->ops, &key)) != NULL)
232 {
233 cupsdLogMessage(CUPSD_LOG_DEBUG2,
234 "cupsdFindPolicyOp: Found exact match...");
235 return (po);
236 }
237
238 key.op = IPP_ANY_OPERATION;
239 if ((po = (cupsd_location_t *)cupsArrayFind(p->ops, &key)) != NULL)
240 {
241 cupsdLogMessage(CUPSD_LOG_DEBUG2,
242 "cupsdFindPolicyOp: Found wildcard match...");
243 return (po);
244 }
245
246 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindPolicyOp: No match found.");
247
248 return (NULL);
249 }
250
251
252 /*
253 * 'cupsdGetPrivateAttrs()' - Get the private attributes for the current
254 * request.
255 */
256
257 cups_array_t * /* O - Array or NULL for no restrictions */
cupsdGetPrivateAttrs(cupsd_policy_t * policy,cupsd_client_t * con,cupsd_printer_t * printer,const char * owner)258 cupsdGetPrivateAttrs(
259 cupsd_policy_t *policy, /* I - Policy */
260 cupsd_client_t *con, /* I - Client connection */
261 cupsd_printer_t *printer, /* I - Printer, if any */
262 const char *owner) /* I - Owner of object */
263 {
264 char *name; /* Current name in access list */
265 cups_array_t *access_ptr, /* Access array */
266 *attrs_ptr; /* Attributes array */
267 const char *username; /* Username associated with request */
268 ipp_attribute_t *attr; /* Attribute from request */
269 struct passwd *pw; /* User info */
270
271
272 #ifdef DEBUG
273 cupsdLogMessage(CUPSD_LOG_DEBUG2,
274 "cupsdGetPrivateAttrs(policy=%p(%s), con=%p(%d), "
275 "printer=%p(%s), owner=\"%s\")", policy, policy ? policy->name : "", con,
276 con->number, printer, printer ? printer->name : "", owner);
277 #endif /* DEBUG */
278
279 if (!policy)
280 {
281 cupsdLogMessage(CUPSD_LOG_CRIT, "cupsdGetPrivateAttrs: policy=%p, con=%p, printer=%p, owner=\"%s\", DefaultPolicyPtr=%p: This should never happen, please report a bug.", policy, con, printer, owner, DefaultPolicyPtr);
282 policy = DefaultPolicyPtr;
283 }
284
285 /*
286 * Get the access and attributes lists that correspond to the request...
287 */
288
289 #ifdef DEBUG
290 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: %s",
291 ippOpString(con->request->request.op.operation_id));
292 #endif /* DEBUG */
293
294 switch (con->request->request.op.operation_id)
295 {
296 case IPP_GET_SUBSCRIPTIONS :
297 case IPP_GET_SUBSCRIPTION_ATTRIBUTES :
298 case IPP_GET_NOTIFICATIONS :
299 access_ptr = policy->sub_access;
300 attrs_ptr = policy->sub_attrs;
301 break;
302
303 default :
304 access_ptr = policy->job_access;
305 attrs_ptr = policy->job_attrs;
306 break;
307 }
308
309 /*
310 * If none of the attributes are private, return NULL now...
311 */
312
313 if ((name = (char *)cupsArrayFirst(attrs_ptr)) != NULL &&
314 !_cups_strcasecmp(name, "none"))
315 {
316 #ifdef DEBUG
317 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: Returning NULL.");
318 #endif /* DEBUG */
319
320 return (NULL);
321 }
322 else if (name && !_cups_strcasecmp(name, "all"))
323 {
324 return (attrs_ptr);
325 }
326
327 /*
328 * Otherwise check the user against the access list...
329 */
330
331 if (con->username[0])
332 username = con->username;
333 else if ((attr = ippFindAttribute(con->request, "requesting-user-name",
334 IPP_TAG_NAME)) != NULL)
335 username = attr->values[0].string.text;
336 else
337 username = "anonymous";
338
339 if (username[0])
340 {
341 pw = getpwnam(username);
342 endpwent();
343 }
344 else
345 pw = NULL;
346
347 #ifdef DEBUG
348 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: username=\"%s\"",
349 username);
350 #endif /* DEBUG */
351
352 /*
353 * Otherwise check the user against the access list...
354 */
355
356 for (name = (char *)cupsArrayFirst(access_ptr);
357 name;
358 name = (char *)cupsArrayNext(access_ptr))
359 {
360 #ifdef DEBUG
361 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: name=%s", name);
362 #endif /* DEBUG */
363
364 if (printer && !_cups_strcasecmp(name, "@ACL"))
365 {
366 char *acl; /* Current ACL user/group */
367
368 for (acl = (char *)cupsArrayFirst(printer->users);
369 acl;
370 acl = (char *)cupsArrayNext(printer->users))
371 {
372 if (acl[0] == '@')
373 {
374 /*
375 * Check group membership...
376 */
377
378 if (cupsdCheckGroup(username, pw, acl + 1))
379 break;
380 }
381 else if (acl[0] == '#')
382 {
383 /*
384 * Check UUID...
385 */
386
387 if (cupsdCheckGroup(username, pw, acl))
388 break;
389 }
390 else if (!_cups_strcasecmp(username, acl))
391 break;
392 }
393 }
394 else if (owner && !_cups_strcasecmp(name, "@OWNER") &&
395 !_cups_strcasecmp(username, owner))
396 {
397 #ifdef DEBUG
398 cupsdLogMessage(CUPSD_LOG_DEBUG2,
399 "cupsdGetPrivateAttrs: Returning NULL.");
400 #endif /* DEBUG */
401
402 return (NULL);
403 }
404 else if (!_cups_strcasecmp(name, "@SYSTEM"))
405 {
406 int i; /* Looping var */
407
408 for (i = 0; i < NumSystemGroups; i ++)
409 if (cupsdCheckGroup(username, pw, SystemGroups[i]))
410 {
411 #ifdef DEBUG
412 cupsdLogMessage(CUPSD_LOG_DEBUG2,
413 "cupsdGetPrivateAttrs: Returning NULL.");
414 #endif /* DEBUG */
415
416 return (NULL);
417 }
418 }
419 else if (name[0] == '@')
420 {
421 if (cupsdCheckGroup(username, pw, name + 1))
422 {
423 #ifdef DEBUG
424 cupsdLogMessage(CUPSD_LOG_DEBUG2,
425 "cupsdGetPrivateAttrs: Returning NULL.");
426 #endif /* DEBUG */
427
428 return (NULL);
429 }
430 }
431 else if (!_cups_strcasecmp(username, name))
432 {
433 #ifdef DEBUG
434 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: Returning NULL.");
435 #endif /* DEBUG */
436
437 return (NULL);
438 }
439 }
440
441 /*
442 * No direct access, so return private attributes list...
443 */
444
445 #ifdef DEBUG
446 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: Returning list.");
447 #endif /* DEBUG */
448
449 return (attrs_ptr);
450 }
451
452
453 /*
454 * 'compare_ops()' - Compare two operations.
455 */
456
457 static int /* O - Result of comparison */
compare_ops(cupsd_location_t * a,cupsd_location_t * b)458 compare_ops(cupsd_location_t *a, /* I - First operation */
459 cupsd_location_t *b) /* I - Second operation */
460 {
461 return (a->op - b->op);
462 }
463
464
465 /*
466 * 'compare_policies()' - Compare two policies.
467 */
468
469 static int /* O - Result of comparison */
compare_policies(cupsd_policy_t * a,cupsd_policy_t * b)470 compare_policies(cupsd_policy_t *a, /* I - First policy */
471 cupsd_policy_t *b) /* I - Second policy */
472 {
473 return (_cups_strcasecmp(a->name, b->name));
474 }
475
476
477 /*
478 * 'free_policy()' - Free the memory used by a policy.
479 */
480
481 static void
free_policy(cupsd_policy_t * p)482 free_policy(cupsd_policy_t *p) /* I - Policy to free */
483 {
484 cupsArrayDelete(p->job_access);
485 cupsArrayDelete(p->job_attrs);
486 cupsArrayDelete(p->sub_access);
487 cupsArrayDelete(p->sub_attrs);
488 cupsArrayDelete(p->ops);
489 cupsdClearString(&p->name);
490 free(p);
491 }
492
493
494 /*
495 * 'hash_op()' - Generate a lookup hash for the operation.
496 */
497
498 static int /* O - Hash value */
hash_op(cupsd_location_t * op)499 hash_op(cupsd_location_t *op) /* I - Operation */
500 {
501 return (((op->op >> 6) & 0x40) | (op->op & 0x3f));
502 }
503