| /* |
| * Policy routines for the CUPS scheduler. |
| * |
| * Copyright 2007-2011, 2014 by Apple Inc. |
| * Copyright 1997-2006 by Easy Software Products, all rights reserved. |
| * |
| * Licensed under Apache License v2.0. See the file "LICENSE" for more information. |
| */ |
| |
| /* |
| * Include necessary headers... |
| */ |
| |
| #include "cupsd.h" |
| #include <pwd.h> |
| |
| |
| /* |
| * Local functions... |
| */ |
| |
| static int compare_ops(cupsd_location_t *a, cupsd_location_t *b); |
| static int compare_policies(cupsd_policy_t *a, cupsd_policy_t *b); |
| static void free_policy(cupsd_policy_t *p); |
| static int hash_op(cupsd_location_t *op); |
| |
| |
| /* |
| * 'cupsdAddPolicy()' - Add a policy to the system. |
| */ |
| |
| cupsd_policy_t * /* O - Policy */ |
| cupsdAddPolicy(const char *policy) /* I - Name of policy */ |
| { |
| cupsd_policy_t *temp; /* Pointer to policy */ |
| |
| |
| if (!policy) |
| return (NULL); |
| |
| if (!Policies) |
| Policies = cupsArrayNew3((cups_array_func_t)compare_policies, NULL, |
| (cups_ahash_func_t)NULL, 0, |
| (cups_acopy_func_t)NULL, |
| (cups_afree_func_t)free_policy); |
| |
| if (!Policies) |
| return (NULL); |
| |
| if ((temp = calloc(1, sizeof(cupsd_policy_t))) != NULL) |
| { |
| cupsdSetString(&temp->name, policy); |
| cupsArrayAdd(Policies, temp); |
| } |
| |
| return (temp); |
| } |
| |
| |
| /* |
| * 'cupsdAddPolicyOp()' - Add an operation to a policy. |
| */ |
| |
| cupsd_location_t * /* O - New policy operation */ |
| cupsdAddPolicyOp(cupsd_policy_t *p, /* I - Policy */ |
| cupsd_location_t *po, /* I - Policy operation to copy */ |
| ipp_op_t op) /* I - IPP operation code */ |
| { |
| cupsd_location_t *temp; /* New policy operation */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddPolicyOp(p=%p, po=%p, op=%x(%s))", |
| p, po, op, ippOpString(op)); |
| |
| if (!p) |
| return (NULL); |
| |
| if (!p->ops) |
| p->ops = cupsArrayNew3((cups_array_func_t)compare_ops, NULL, |
| (cups_ahash_func_t)hash_op, 128, |
| (cups_acopy_func_t)NULL, |
| (cups_afree_func_t)cupsdFreeLocation); |
| |
| if (!p->ops) |
| return (NULL); |
| |
| if ((temp = cupsdCopyLocation(po)) != NULL) |
| { |
| temp->op = op; |
| temp->limit = CUPSD_AUTH_LIMIT_IPP; |
| |
| cupsArrayAdd(p->ops, temp); |
| } |
| |
| return (temp); |
| } |
| |
| |
| /* |
| * 'cupsdCheckPolicy()' - Check the IPP operation and username against a policy. |
| */ |
| |
| http_status_t /* I - 1 if OK, 0 otherwise */ |
| cupsdCheckPolicy(cupsd_policy_t *p, /* I - Policy */ |
| cupsd_client_t *con, /* I - Client connection */ |
| const char *owner) /* I - Owner of object */ |
| { |
| cupsd_location_t *po; /* Current policy operation */ |
| |
| |
| /* |
| * Range check... |
| */ |
| |
| if (!p || !con) |
| { |
| cupsdLogMessage(CUPSD_LOG_CRIT, "cupsdCheckPolicy: p=%p, con=%p.", p, con); |
| |
| return ((http_status_t)0); |
| } |
| |
| /* |
| * Find a match for the operation... |
| */ |
| |
| if ((po = cupsdFindPolicyOp(p, con->request->request.op.operation_id)) == NULL) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckPolicy: No matching operation, returning 0."); |
| return ((http_status_t)0); |
| } |
| |
| con->best = po; |
| |
| /* |
| * Return the status of the check... |
| */ |
| |
| return (cupsdIsAuthorized(con, owner)); |
| } |
| |
| |
| /* |
| * 'cupsdDeleteAllPolicies()' - Delete all policies in memory. |
| */ |
| |
| void |
| cupsdDeleteAllPolicies(void) |
| { |
| cupsd_printer_t *printer; /* Current printer */ |
| |
| |
| if (!Policies) |
| return; |
| |
| /* |
| * First clear the policy pointers for all printers... |
| */ |
| |
| for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers); |
| printer; |
| printer = (cupsd_printer_t *)cupsArrayNext(Printers)) |
| printer->op_policy_ptr = NULL; |
| |
| DefaultPolicyPtr = NULL; |
| |
| /* |
| * Then free all of the policies... |
| */ |
| |
| cupsArrayDelete(Policies); |
| |
| Policies = NULL; |
| } |
| |
| |
| /* |
| * 'cupsdFindPolicy()' - Find a named policy. |
| */ |
| |
| cupsd_policy_t * /* O - Policy */ |
| cupsdFindPolicy(const char *policy) /* I - Name of policy */ |
| { |
| cupsd_policy_t key; /* Search key */ |
| |
| |
| /* |
| * Range check... |
| */ |
| |
| if (!policy) |
| return (NULL); |
| |
| /* |
| * Look it up... |
| */ |
| |
| key.name = (char *)policy; |
| return ((cupsd_policy_t *)cupsArrayFind(Policies, &key)); |
| } |
| |
| |
| /* |
| * 'cupsdFindPolicyOp()' - Find a policy operation. |
| */ |
| |
| cupsd_location_t * /* O - Policy operation */ |
| cupsdFindPolicyOp(cupsd_policy_t *p, /* I - Policy */ |
| ipp_op_t op) /* I - IPP operation */ |
| { |
| cupsd_location_t key, /* Search key... */ |
| *po; /* Current policy operation */ |
| |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindPolicyOp(p=%p, op=%x(%s))", |
| p, op, ippOpString(op)); |
| |
| /* |
| * Range check... |
| */ |
| |
| if (!p) |
| return (NULL); |
| |
| /* |
| * Check the operation against the available policies... |
| */ |
| |
| key.op = op; |
| if ((po = (cupsd_location_t *)cupsArrayFind(p->ops, &key)) != NULL) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdFindPolicyOp: Found exact match..."); |
| return (po); |
| } |
| |
| key.op = IPP_ANY_OPERATION; |
| if ((po = (cupsd_location_t *)cupsArrayFind(p->ops, &key)) != NULL) |
| { |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdFindPolicyOp: Found wildcard match..."); |
| return (po); |
| } |
| |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindPolicyOp: No match found."); |
| |
| return (NULL); |
| } |
| |
| |
| /* |
| * 'cupsdGetPrivateAttrs()' - Get the private attributes for the current |
| * request. |
| */ |
| |
| cups_array_t * /* O - Array or NULL for no restrictions */ |
| cupsdGetPrivateAttrs( |
| cupsd_policy_t *policy, /* I - Policy */ |
| cupsd_client_t *con, /* I - Client connection */ |
| cupsd_printer_t *printer, /* I - Printer, if any */ |
| const char *owner) /* I - Owner of object */ |
| { |
| char *name; /* Current name in access list */ |
| cups_array_t *access_ptr, /* Access array */ |
| *attrs_ptr; /* Attributes array */ |
| const char *username; /* Username associated with request */ |
| ipp_attribute_t *attr; /* Attribute from request */ |
| struct passwd *pw; /* User info */ |
| |
| |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdGetPrivateAttrs(policy=%p(%s), con=%p(%d), " |
| "printer=%p(%s), owner=\"%s\")", policy, policy->name, con, |
| con->number, printer, printer ? printer->name : "", owner); |
| #endif /* DEBUG */ |
| |
| if (!policy) |
| { |
| 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); |
| policy = DefaultPolicyPtr; |
| } |
| |
| /* |
| * Get the access and attributes lists that correspond to the request... |
| */ |
| |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: %s", |
| ippOpString(con->request->request.op.operation_id)); |
| #endif /* DEBUG */ |
| |
| switch (con->request->request.op.operation_id) |
| { |
| case IPP_GET_SUBSCRIPTIONS : |
| case IPP_GET_SUBSCRIPTION_ATTRIBUTES : |
| case IPP_GET_NOTIFICATIONS : |
| access_ptr = policy->sub_access; |
| attrs_ptr = policy->sub_attrs; |
| break; |
| |
| default : |
| access_ptr = policy->job_access; |
| attrs_ptr = policy->job_attrs; |
| break; |
| } |
| |
| /* |
| * If none of the attributes are private, return NULL now... |
| */ |
| |
| if ((name = (char *)cupsArrayFirst(attrs_ptr)) != NULL && |
| !_cups_strcasecmp(name, "none")) |
| { |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: Returning NULL."); |
| #endif /* DEBUG */ |
| |
| return (NULL); |
| } |
| |
| /* |
| * Otherwise check the user against the access list... |
| */ |
| |
| if (con->username[0]) |
| username = con->username; |
| else if ((attr = ippFindAttribute(con->request, "requesting-user-name", |
| IPP_TAG_NAME)) != NULL) |
| username = attr->values[0].string.text; |
| else |
| username = "anonymous"; |
| |
| if (username[0]) |
| { |
| pw = getpwnam(username); |
| endpwent(); |
| } |
| else |
| pw = NULL; |
| |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: username=\"%s\"", |
| username); |
| #endif /* DEBUG */ |
| |
| /* |
| * Otherwise check the user against the access list... |
| */ |
| |
| for (name = (char *)cupsArrayFirst(access_ptr); |
| name; |
| name = (char *)cupsArrayNext(access_ptr)) |
| { |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: name=%s", name); |
| #endif /* DEBUG */ |
| |
| if (printer && !_cups_strcasecmp(name, "@ACL")) |
| { |
| char *acl; /* Current ACL user/group */ |
| |
| for (acl = (char *)cupsArrayFirst(printer->users); |
| acl; |
| acl = (char *)cupsArrayNext(printer->users)) |
| { |
| if (acl[0] == '@') |
| { |
| /* |
| * Check group membership... |
| */ |
| |
| if (cupsdCheckGroup(username, pw, acl + 1)) |
| break; |
| } |
| else if (acl[0] == '#') |
| { |
| /* |
| * Check UUID... |
| */ |
| |
| if (cupsdCheckGroup(username, pw, acl)) |
| break; |
| } |
| else if (!_cups_strcasecmp(username, acl)) |
| break; |
| } |
| } |
| else if (owner && !_cups_strcasecmp(name, "@OWNER") && |
| !_cups_strcasecmp(username, owner)) |
| { |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdGetPrivateAttrs: Returning NULL."); |
| #endif /* DEBUG */ |
| |
| return (NULL); |
| } |
| else if (!_cups_strcasecmp(name, "@SYSTEM")) |
| { |
| int i; /* Looping var */ |
| |
| for (i = 0; i < NumSystemGroups; i ++) |
| if (cupsdCheckGroup(username, pw, SystemGroups[i])) |
| { |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdGetPrivateAttrs: Returning NULL."); |
| #endif /* DEBUG */ |
| |
| return (NULL); |
| } |
| } |
| else if (name[0] == '@') |
| { |
| if (cupsdCheckGroup(username, pw, name + 1)) |
| { |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, |
| "cupsdGetPrivateAttrs: Returning NULL."); |
| #endif /* DEBUG */ |
| |
| return (NULL); |
| } |
| } |
| else if (!_cups_strcasecmp(username, name)) |
| { |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: Returning NULL."); |
| #endif /* DEBUG */ |
| |
| return (NULL); |
| } |
| } |
| |
| /* |
| * No direct access, so return private attributes list... |
| */ |
| |
| #ifdef DEBUG |
| cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdGetPrivateAttrs: Returning list."); |
| #endif /* DEBUG */ |
| |
| return (attrs_ptr); |
| } |
| |
| |
| /* |
| * 'compare_ops()' - Compare two operations. |
| */ |
| |
| static int /* O - Result of comparison */ |
| compare_ops(cupsd_location_t *a, /* I - First operation */ |
| cupsd_location_t *b) /* I - Second operation */ |
| { |
| return (a->op - b->op); |
| } |
| |
| |
| /* |
| * 'compare_policies()' - Compare two policies. |
| */ |
| |
| static int /* O - Result of comparison */ |
| compare_policies(cupsd_policy_t *a, /* I - First policy */ |
| cupsd_policy_t *b) /* I - Second policy */ |
| { |
| return (_cups_strcasecmp(a->name, b->name)); |
| } |
| |
| |
| /* |
| * 'free_policy()' - Free the memory used by a policy. |
| */ |
| |
| static void |
| free_policy(cupsd_policy_t *p) /* I - Policy to free */ |
| { |
| cupsArrayDelete(p->job_access); |
| cupsArrayDelete(p->job_attrs); |
| cupsArrayDelete(p->sub_access); |
| cupsArrayDelete(p->sub_attrs); |
| cupsArrayDelete(p->ops); |
| cupsdClearString(&p->name); |
| free(p); |
| } |
| |
| |
| /* |
| * 'hash_op()' - Generate a lookup hash for the operation. |
| */ |
| |
| static int /* O - Hash value */ |
| hash_op(cupsd_location_t *op) /* I - Operation */ |
| { |
| return (((op->op >> 6) & 0x40) | (op->op & 0x3f)); |
| } |