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