From: Michael R Sweet Date: Tue, 5 Oct 2021 20:50:39 +0000 (-0400) Subject: Fix some clang-reported issues. X-Git-Tag: v2.4b1~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=88a332344ada8926bf4c8c46ce79be61f76f2993;p=thirdparty%2Fcups.git Fix some clang-reported issues. --- diff --git a/backend/ipp.c b/backend/ipp.c index 3779c88fa4..6964e4535f 100644 --- a/backend/ipp.c +++ b/backend/ipp.c @@ -1431,7 +1431,7 @@ main(int argc, /* I - Number of command-line args */ */ if (version == 10) - create_job = send_document = 0; + create_job = 0; /* * Start monitoring the printer in the background... @@ -1497,9 +1497,7 @@ main(int argc, /* I - Number of command-line args */ * One or more options are not supported... */ - ipp_attribute_t *attr; /* Unsupported attribute */ - - if ((attr = ippFindAttribute(response, "sides", IPP_TAG_ZERO)) != NULL) + if (ippFindAttribute(response, "sides", IPP_TAG_ZERO)) { /* * The sides value is not supported, revert to one-sided as needed... diff --git a/cups/dest.c b/cups/dest.c index 60a9539307..fa6202c772 100644 --- a/cups/dest.c +++ b/cups/dest.c @@ -274,7 +274,7 @@ cupsAddDest(const char *name, /* I - Destination name */ if (instance && !cupsGetDest(name, NULL, num_dests, *dests)) { // Add destination first... - if ((dest = cups_add_dest(name, NULL, &num_dests, dests)) == NULL) + if (!cups_add_dest(name, NULL, &num_dests, dests)) return (num_dests); } diff --git a/notifier/rss.c b/notifier/rss.c index 516b615458..3efc3f9ece 100644 --- a/notifier/rss.c +++ b/notifier/rss.c @@ -566,7 +566,17 @@ new_message(int sequence_number, /* I - notify-sequence-number */ if ((msg = calloc(1, sizeof(_cups_rss_t))) == NULL) + { +#ifdef __clang_analyzer__ + // These free calls are really unnecessary (a failure here ultimately causes + // an exit, which frees all memory much faster) but it makes Clang happy... + free(subject); + free(text); + free(link_url); +#endif // __clang_analyzer__ + return (NULL); + } msg->sequence_number = sequence_number; msg->subject = subject; diff --git a/ppdc/ppdc-import.cxx b/ppdc/ppdc-import.cxx index b4a8341386..d12d0c244e 100644 --- a/ppdc/ppdc-import.cxx +++ b/ppdc/ppdc-import.cxx @@ -57,274 +57,276 @@ ppdcSource::import_ppd(const char *f) // I - Filename } // See if the driver has already been imported... - if ((driver = find_driver(ppd->pcfilename)) == NULL) + if (find_driver(ppd->pcfilename)) { - // Create a new PPD file... - if ((fp = cupsFileOpen(f, "r")) == NULL) - { - ppdClose(ppd); - return (0); - } - - driver = new ppdcDriver(); - driver->type = PPDC_DRIVER_PS; - - drivers->add(driver); + ppdClose(ppd); + return (1); + } - // Read the initial comments from the PPD file and use them as the - // copyright/license text... - cupsFileGets(fp, line, sizeof(line)); - // Skip *PPD-Adobe-M.m + // Create a new PPD file... + if ((fp = cupsFileOpen(f, "r")) == NULL) + { + ppdClose(ppd); + return (0); + } - while (cupsFileGets(fp, line, sizeof(line))) - if (strncmp(line, "*%", 2)) - break; - else if (strncmp(line, "*%%%% ", 6)) - { - for (ptr = line + 2; isspace(*ptr); ptr ++); + driver = new ppdcDriver(); + driver->type = PPDC_DRIVER_PS; - driver->add_copyright(ptr); - } + drivers->add(driver); - cupsFileClose(fp); + // Read the initial comments from the PPD file and use them as the + // copyright/license text... + cupsFileGets(fp, line, sizeof(line)); + // Skip *PPD-Adobe-M.m - // Then add the stuff from the PPD file... - if (ppd->modelname && ppd->manufacturer && - !_cups_strncasecmp(ppd->modelname, ppd->manufacturer, - strlen(ppd->manufacturer))) + while (cupsFileGets(fp, line, sizeof(line))) + if (strncmp(line, "*%", 2)) + break; + else if (strncmp(line, "*%%%% ", 6)) { - ptr = ppd->modelname + strlen(ppd->manufacturer); + for (ptr = line + 2; isspace(*ptr); ptr ++); - while (isspace(*ptr)) - ptr ++; + driver->add_copyright(ptr); } - else - ptr = ppd->modelname; - - if (ppd->nickname) - driver->add_attr(new ppdcAttr("NickName", NULL, NULL, ppd->nickname)); - - if (ppd->shortnickname) - driver->add_attr(new ppdcAttr("ShortNickName", NULL, NULL, - ppd->shortnickname)); - - driver->manufacturer = new ppdcString(ppd->manufacturer); - driver->model_name = new ppdcString(ptr); - driver->pc_file_name = new ppdcString(ppd->pcfilename); - attr = ppdFindAttr(ppd, "FileVersion", NULL); - driver->version = new ppdcString(attr ? attr->value : NULL); - driver->model_number = ppd->model_number; - driver->manual_copies = ppd->manual_copies; - driver->color_device = ppd->color_device; - driver->throughput = ppd->throughput; - driver->variable_paper_size = ppd->variable_sizes; - driver->max_width = ppd->custom_max[0]; - driver->max_length = ppd->custom_max[1]; - driver->min_width = ppd->custom_min[0]; - driver->min_length = ppd->custom_min[1]; - driver->left_margin = ppd->custom_margins[0]; - driver->bottom_margin = ppd->custom_margins[1]; - driver->right_margin = ppd->custom_margins[2]; - driver->top_margin = ppd->custom_margins[3]; - - for (i = 0; i < ppd->num_filters; i ++) - { - strlcpy(line, ppd->filters[i], sizeof(line)); - for (ptr = line; *ptr; ptr ++) - if (isspace(*ptr & 255)) - break; - *ptr++ = '\0'; + cupsFileClose(fp); + + // Then add the stuff from the PPD file... + if (ppd->modelname && ppd->manufacturer && + !_cups_strncasecmp(ppd->modelname, ppd->manufacturer, + strlen(ppd->manufacturer))) + { + ptr = ppd->modelname + strlen(ppd->manufacturer); - cost = strtol(ptr, &ptr, 10); + while (isspace(*ptr)) + ptr ++; + } + else + ptr = ppd->modelname; + + if (ppd->nickname) + driver->add_attr(new ppdcAttr("NickName", NULL, NULL, ppd->nickname)); + + if (ppd->shortnickname) + driver->add_attr(new ppdcAttr("ShortNickName", NULL, NULL, + ppd->shortnickname)); + + driver->manufacturer = new ppdcString(ppd->manufacturer); + driver->model_name = new ppdcString(ptr); + driver->pc_file_name = new ppdcString(ppd->pcfilename); + attr = ppdFindAttr(ppd, "FileVersion", NULL); + driver->version = new ppdcString(attr ? attr->value : NULL); + driver->model_number = ppd->model_number; + driver->manual_copies = ppd->manual_copies; + driver->color_device = ppd->color_device; + driver->throughput = ppd->throughput; + driver->variable_paper_size = ppd->variable_sizes; + driver->max_width = ppd->custom_max[0]; + driver->max_length = ppd->custom_max[1]; + driver->min_width = ppd->custom_min[0]; + driver->min_length = ppd->custom_min[1]; + driver->left_margin = ppd->custom_margins[0]; + driver->bottom_margin = ppd->custom_margins[1]; + driver->right_margin = ppd->custom_margins[2]; + driver->top_margin = ppd->custom_margins[3]; + + for (i = 0; i < ppd->num_filters; i ++) + { + strlcpy(line, ppd->filters[i], sizeof(line)); - while (isspace(*ptr & 255)) - ptr ++; + for (ptr = line; *ptr; ptr ++) + if (isspace(*ptr & 255)) + break; + *ptr++ = '\0'; - filter = new ppdcFilter(line, ptr, cost); - driver->add_filter(filter); - } + cost = strtol(ptr, &ptr, 10); - attr = ppdFindAttr(ppd, "DefaultFont", NULL); - driver->default_font = new ppdcString(attr ? attr->value : NULL); + while (isspace(*ptr & 255)) + ptr ++; - // Collect media sizes... - ppd_option_t *region_option, // PageRegion option - *size_option; // PageSize option - ppd_choice_t *region_choice, // PageRegion choice - *size_choice; // PageSize choice + filter = new ppdcFilter(line, ptr, cost); + driver->add_filter(filter); + } - region_option = ppdFindOption(ppd, "PageRegion"); - size_option = ppdFindOption(ppd, "PageSize"); + attr = ppdFindAttr(ppd, "DefaultFont", NULL); + driver->default_font = new ppdcString(attr ? attr->value : NULL); - for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) - { - // Don't do custom size here... - if (!_cups_strcasecmp(size->name, "Custom")) - continue; + // Collect media sizes... + ppd_option_t *region_option, // PageRegion option + *size_option; // PageSize option + ppd_choice_t *region_choice, // PageRegion choice + *size_choice; // PageSize choice - // Get the code for the PageSize and PageRegion options... - region_choice = ppdFindChoice(region_option, size->name); - size_choice = ppdFindChoice(size_option, size->name); + region_option = ppdFindOption(ppd, "PageRegion"); + size_option = ppdFindOption(ppd, "PageSize"); - // Create a new media size record and add it to the driver... - csize = new ppdcMediaSize(size->name, size_choice->text, size->width, - size->length, size->left, size->bottom, - size->width - size->right, - size->length - size->top, - size_choice->code, region_choice->code); + for (i = ppd->num_sizes, size = ppd->sizes; i > 0; i --, size ++) + { + // Don't do custom size here... + if (!_cups_strcasecmp(size->name, "Custom")) + continue; - driver->add_size(csize); + // Get the code for the PageSize and PageRegion options... + region_choice = ppdFindChoice(region_option, size->name); + size_choice = ppdFindChoice(size_option, size->name); - if (!_cups_strcasecmp(size_option->defchoice, size->name)) - driver->set_default_size(csize); - } + // Create a new media size record and add it to the driver... + csize = new ppdcMediaSize(size->name, size_choice->text, size->width, + size->length, size->left, size->bottom, + size->width - size->right, + size->length - size->top, + size_choice->code, region_choice->code); - // Now all of the options... - for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) - { - cgroup = new ppdcGroup(group->name, group->text); - driver->add_group(cgroup); + driver->add_size(csize); - for (j = group->num_options, option = group->options; j > 0; j --, option ++) - { - if (!strcmp(option->keyword, "PageSize") || !strcmp(option->keyword, "PageRegion")) - continue; + if (!_cups_strcasecmp(size_option->defchoice, size->name)) + driver->set_default_size(csize); + } - coption = new ppdcOption((ppdcOptType)option->ui, option->keyword, - option->text, (ppdcOptSection)option->section, - option->order); - cgroup->add_option(coption); + // Now all of the options... + for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++) + { + cgroup = new ppdcGroup(group->name, group->text); + driver->add_group(cgroup); - for (k = option->num_choices, choice = option->choices; k > 0; k --, choice ++) - { - if (!strcmp(choice->choice, "Custom")) - continue; + for (j = group->num_options, option = group->options; j > 0; j --, option ++) + { + if (!strcmp(option->keyword, "PageSize") || !strcmp(option->keyword, "PageRegion")) + continue; - cchoice = new ppdcChoice(choice->choice, choice->text, choice->code); - coption->add_choice(cchoice); + coption = new ppdcOption((ppdcOptType)option->ui, option->keyword, + option->text, (ppdcOptSection)option->section, + option->order); + cgroup->add_option(coption); - if (!_cups_strcasecmp(option->defchoice, choice->choice)) - coption->set_defchoice(cchoice); - } + for (k = option->num_choices, choice = option->choices; k > 0; k --, choice ++) + { + if (!strcmp(choice->choice, "Custom")) + continue; + + cchoice = new ppdcChoice(choice->choice, choice->text, choice->code); + coption->add_choice(cchoice); + + if (!_cups_strcasecmp(option->defchoice, choice->choice)) + coption->set_defchoice(cchoice); } } + } - // Now the constraints... - for (i = ppd->num_consts, constraint = ppd->consts; - i > 0; - i --, constraint ++) - { - // Look for mirrored constraints... - for (j = i - 1, constraint2 = constraint + 1; - j > 0; - j --, constraint2 ++) - if (!strcmp(constraint->option1, constraint2->option2) && - !strcmp(constraint->choice1, constraint2->choice2) && - !strcmp(constraint->option2, constraint2->option1) && - !strcmp(constraint->choice2, constraint2->choice1)) - break; - - if (j) - continue; - - cconstraint = new ppdcConstraint(constraint->option2, constraint->choice2, - constraint->option1, constraint->choice1); - driver->add_constraint(cconstraint); - } + // Now the constraints... + for (i = ppd->num_consts, constraint = ppd->consts; + i > 0; + i --, constraint ++) + { + // Look for mirrored constraints... + for (j = i - 1, constraint2 = constraint + 1; + j > 0; + j --, constraint2 ++) + if (!strcmp(constraint->option1, constraint2->option2) && + !strcmp(constraint->choice1, constraint2->choice2) && + !strcmp(constraint->option2, constraint2->option1) && + !strcmp(constraint->choice2, constraint2->choice1)) + break; + + if (j) + continue; + + cconstraint = new ppdcConstraint(constraint->option2, constraint->choice2, + constraint->option1, constraint->choice1); + driver->add_constraint(cconstraint); + } + + for (i = 0; i < ppd->num_attrs; i ++) + { + attr = ppd->attrs[i]; - for (i = 0; i < ppd->num_attrs; i ++) + if (!strcmp(attr->name, "Font")) { - attr = ppd->attrs[i]; + // Font... + char encoding[256], // Encoding string + version[256], // Version string + charset[256], // Charset string + status[256]; // Status string + ppdcFontStatus fstatus; // Status enumeration + - if (!strcmp(attr->name, "Font")) + if (sscanf(attr->value, "%s%*[^\"]\"%[^\"]\"%s%s", encoding, version, + charset, status) != 4) { - // Font... - char encoding[256], // Encoding string - version[256], // Version string - charset[256], // Charset string - status[256]; // Status string - ppdcFontStatus fstatus; // Status enumeration - - - if (sscanf(attr->value, "%s%*[^\"]\"%[^\"]\"%s%s", encoding, version, - charset, status) != 4) - { - _cupsLangPrintf(stderr, _("ppdc: Bad font attribute: %s"), - attr->value); - continue; - } + _cupsLangPrintf(stderr, _("ppdc: Bad font attribute: %s"), + attr->value); + continue; + } - if (!strcmp(status, "ROM")) - fstatus = PPDC_FONT_ROM; - else - fstatus = PPDC_FONT_DISK; + if (!strcmp(status, "ROM")) + fstatus = PPDC_FONT_ROM; + else + fstatus = PPDC_FONT_DISK; - font = new ppdcFont(attr->spec, encoding, version, charset, fstatus); + font = new ppdcFont(attr->spec, encoding, version, charset, fstatus); - driver->add_font(font); - } - else if (!strcmp(attr->name, "CustomPageSize")) - { - driver->set_custom_size_code(attr->value); - } - else if ((strncmp(attr->name, "Default", 7) || - !strcmp(attr->name, "DefaultColorSpace")) && - strcmp(attr->name, "ColorDevice") && - strcmp(attr->name, "Manufacturer") && - strcmp(attr->name, "ModelName") && - strcmp(attr->name, "MaxMediaHeight") && - strcmp(attr->name, "MaxMediaWidth") && - strcmp(attr->name, "NickName") && - strcmp(attr->name, "ParamCustomPageSize") && - strcmp(attr->name, "ShortNickName") && - strcmp(attr->name, "Throughput") && - strcmp(attr->name, "PCFileName") && - strcmp(attr->name, "FileVersion") && - strcmp(attr->name, "FormatVersion") && - strcmp(attr->name, "HWMargins") && - strcmp(attr->name, "VariablePaperSize") && - strcmp(attr->name, "LanguageEncoding") && - strcmp(attr->name, "LanguageVersion") && - strcmp(attr->name, "cupsFilter") && - strcmp(attr->name, "cupsFlipDuplex") && - strcmp(attr->name, "cupsLanguages") && - strcmp(attr->name, "cupsManualCopies") && - strcmp(attr->name, "cupsModelNumber") && - strcmp(attr->name, "cupsVersion")) - { - if ((ptr = strchr(attr->name, '.')) != NULL && - ((ptr - attr->name) == 2 || (ptr - attr->name) == 5)) - { - // Might be a localization attribute; test further... - if (isalpha(attr->name[0] & 255) && - isalpha(attr->name[1] & 255) && - (attr->name[2] == '.' || - (attr->name[2] == '_' && isalpha(attr->name[3] & 255) && - isalpha(attr->name[4] & 255)))) - continue; - } - - // Attribute... - driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text, - attr->value)); - } - else if (!strncmp(attr->name, "Default", 7) && - !ppdFindOption(ppd, attr->name + 7) && - strcmp(attr->name, "DefaultFont") && - strcmp(attr->name, "DefaultImageableArea") && - strcmp(attr->name, "DefaultPaperDimension") && - strcmp(attr->name, "DefaultFont")) + driver->add_font(font); + } + else if (!strcmp(attr->name, "CustomPageSize")) + { + driver->set_custom_size_code(attr->value); + } + else if ((strncmp(attr->name, "Default", 7) || + !strcmp(attr->name, "DefaultColorSpace")) && + strcmp(attr->name, "ColorDevice") && + strcmp(attr->name, "Manufacturer") && + strcmp(attr->name, "ModelName") && + strcmp(attr->name, "MaxMediaHeight") && + strcmp(attr->name, "MaxMediaWidth") && + strcmp(attr->name, "NickName") && + strcmp(attr->name, "ParamCustomPageSize") && + strcmp(attr->name, "ShortNickName") && + strcmp(attr->name, "Throughput") && + strcmp(attr->name, "PCFileName") && + strcmp(attr->name, "FileVersion") && + strcmp(attr->name, "FormatVersion") && + strcmp(attr->name, "HWMargins") && + strcmp(attr->name, "VariablePaperSize") && + strcmp(attr->name, "LanguageEncoding") && + strcmp(attr->name, "LanguageVersion") && + strcmp(attr->name, "cupsFilter") && + strcmp(attr->name, "cupsFlipDuplex") && + strcmp(attr->name, "cupsLanguages") && + strcmp(attr->name, "cupsManualCopies") && + strcmp(attr->name, "cupsModelNumber") && + strcmp(attr->name, "cupsVersion")) + { + if ((ptr = strchr(attr->name, '.')) != NULL && + ((ptr - attr->name) == 2 || (ptr - attr->name) == 5)) { - // Default attribute... - driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text, - attr->value)); + // Might be a localization attribute; test further... + if (isalpha(attr->name[0] & 255) && + isalpha(attr->name[1] & 255) && + (attr->name[2] == '.' || + (attr->name[2] == '_' && isalpha(attr->name[3] & 255) && + isalpha(attr->name[4] & 255)))) + continue; } + + // Attribute... + driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text, + attr->value)); + } + else if (!strncmp(attr->name, "Default", 7) && + !ppdFindOption(ppd, attr->name + 7) && + strcmp(attr->name, "DefaultFont") && + strcmp(attr->name, "DefaultImageableArea") && + strcmp(attr->name, "DefaultPaperDimension") && + strcmp(attr->name, "DefaultFont")) + { + // Default attribute... + driver->add_attr(new ppdcAttr(attr->name, attr->spec, attr->text, + attr->value)); } } - if (ppd) - ppdClose(ppd); + ppdClose(ppd); return (1); } diff --git a/ppdc/ppdc-source.cxx b/ppdc/ppdc-source.cxx index d1e942662d..0a0e40592e 100644 --- a/ppdc/ppdc-source.cxx +++ b/ppdc/ppdc-source.cxx @@ -2792,7 +2792,7 @@ ppdcSource::scan_file(ppdcFile *fp, // I - File to read if (have_cutter <= 0 || cond_state) continue; - if ((o = d->find_option("CutMedia")) == NULL) + if (!d->find_option("CutMedia")) { o = new ppdcOption(PPDC_BOOLEAN, "CutMedia", "Cut Media", PPDC_SECTION_ANY, 10.0f); @@ -2805,9 +2805,8 @@ ppdcSource::scan_file(ppdcFile *fp, // I - File to read c = new ppdcChoice("True", NULL, "<>setpagedevice"); o->add_choice(c); + o = NULL; } - - o = NULL; } else if (!_cups_strcasecmp(temp, "Darkness")) { diff --git a/ppdc/ppdhtml.cxx b/ppdc/ppdhtml.cxx index 75636a9447..cb2a784b2b 100644 --- a/ppdc/ppdhtml.cxx +++ b/ppdc/ppdhtml.cxx @@ -80,7 +80,6 @@ main(int argc, // I - Number of command-line arguments default : // Unknown usage(); - break; } } else @@ -100,7 +99,7 @@ main(int argc, // I - Number of command-line arguments for (g = (ppdcGroup *)d->groups->first(); g; g = (ppdcGroup *)d->groups->next()) for (o = (ppdcOption *)g->options->first(); o; o = (ppdcOption *)g->options->next()) { - if ((compo = composite->find_option(o->name->value)) == NULL) + if (!composite->find_option(o->name->value)) composite->add_option(new ppdcOption(o)); } diff --git a/ppdc/ppdi.cxx b/ppdc/ppdi.cxx index a2f9648f28..f5aa4db520 100644 --- a/ppdc/ppdi.cxx +++ b/ppdc/ppdi.cxx @@ -71,7 +71,6 @@ main(int argc, // I - Number of command-line arguments default : // Unknown usage(); - break; } } else diff --git a/ppdc/ppdmerge.cxx b/ppdc/ppdmerge.cxx index 1ab804399d..cd83964fae 100644 --- a/ppdc/ppdmerge.cxx +++ b/ppdc/ppdmerge.cxx @@ -75,7 +75,6 @@ main(int argc, // I - Number of command-line arguments default : // Unknown usage(); - break; } } else diff --git a/ppdc/ppdpo.cxx b/ppdc/ppdpo.cxx index 498b34e3d7..01f765e442 100644 --- a/ppdc/ppdpo.cxx +++ b/ppdc/ppdpo.cxx @@ -100,7 +100,6 @@ main(int argc, // I - Number of command-line arguments default : // Unknown usage(); - break; } } else diff --git a/scheduler/auth.c b/scheduler/auth.c index 580dcde08e..f29ceb497d 100644 --- a/scheduler/auth.c +++ b/scheduler/auth.c @@ -67,6 +67,7 @@ typedef struct sockpeercred cupsd_ucred_t; * Local functions... */ +static int check_admin_task(cupsd_client_t *con); #ifdef HAVE_AUTHORIZATION_H static int check_authref(cupsd_client_t *con, const char *right); #endif /* HAVE_AUTHORIZATION_H */ @@ -1529,894 +1530,893 @@ cupsdFreeLocation(cupsd_location_t *loc)/* I - Location to free */ /* - * 'cupsdCheckAdminTask()' - Do additional checks on administrative tasks + * 'cupsdIsAuthorized()' - Check to see if the user is authorized... */ -int /* O - 1 if admin task authorized */ -cupsdCheckAdminTask(cupsd_client_t *con) /* I - Connection */ +http_status_t /* O - HTTP_OK if authorized or error code */ +cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */ + const char *owner)/* I - Owner of object */ { - int ret = 1; /* Return value */ - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Administrative task"); + int i, /* Looping vars */ + auth, /* Authorization status */ + type; /* Type of authentication */ + http_addr_t *hostaddr = httpGetAddress(con->http); + /* Client address */ + const char *hostname = httpGetHostname(con->http, NULL, 0); + /* Client hostname */ + unsigned address[4]; /* Authorization address */ + cupsd_location_t *best; /* Best match for location so far */ + size_t hostlen; /* Length of hostname */ + char *name, /* Current username */ + username[256], /* Username to authorize */ + ownername[256], /* Owner name to authorize */ + *ptr; /* Pointer into username */ + struct passwd *pw; /* User password data */ + static const char * const levels[] = /* Auth levels */ + { + "ANON", + "USER", + "GROUP" + }; + static const char * const types[] = /* Auth types */ + { + "None", + "Basic", + "Negotiate" + }; - /* - * If the client accesses locally via domain socket, find out whether it - * is a Snap. Grant access if it is not a Snap, if it is a classic Snap - * or if it is a confined Snap which plugs "cups-control", deny access - * if it is a confined Snap not plugging "cups-control" or if an error - * occurs in the process of finding this out. - */ -#if defined(AF_LOCAL) && defined(SUPPORT_SNAPPED_CLIENTS) + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", con->uri, con->best, con->best ? con->best->location ? con->best->location : "(null)" : ""); + if (owner) + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: owner=\"%s\"", owner); /* - * Get the client's file descriptor and from this its AppArmor context + * If there is no "best" authentication rule for this request, then + * access is allowed from the local system and denied from other + * addresses... */ - if (httpAddrFamily(con->http->hostaddr) == AF_LOCAL) + if (!con->best) { - int peerfd; /* Peer's file descriptor */ - - peerfd = httpGetFd(con->http); - - if (peerfd < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to get peer file descriptor of client connecting via domain socket"); - } + if (httpAddrLocalhost(httpGetAddress(con->http)) || + !strcmp(hostname, ServerName) || + cupsArrayFind(ServerAlias, (void *)hostname)) + return (HTTP_OK); else - { - char *context = NULL; /* AppArmor profile name of client */ -# undef CHECK_METHOD_FOUND -# ifdef SUPPORT_SNAPPED_CUPSD - int status = 65535; /* Status of client Snap context check */ -# if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC) -# define CHECK_METHOD_FOUND 1 - char *args[] = { "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl arguments */ - SnapdClient *client = NULL; /* Data structure of snapd access */ - const char *cookie; /* snapd access cookie */ - GError *error = NULL; /* Glib error */ -# else -# ifdef HAVE_SNAPCTL_IS_CONNECTED -# define CHECK_METHOD_FOUND 1 - char *args[] = { SNAPCTL, "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl command line */ - int fds[2], /* Pipe file descriptors for stderr of - snapctl */ - nullfd; /* /dev/null file descriptor for stdout of - snapctl */ - pid_t pid; /* PID of snapctl */ - cups_file_t *snapctl_stderr; /* CUPS FP for stderr of snapctl */ - char buf[1024]; /* Buffer for snapctl's stderr output */ - int wstatus; /* Wait result of forked snapctl process */ -# endif /* HAVE_SNAPCTL_IS_CONNECTED */ -# endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */ -# else -# if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB) -# define CHECK_METHOD_FOUND 1 - char *snap_name = NULL; /* Client Snap name */ - char *dot; /* Pointer to dot in AppArmor profile name */ - SnapdClient *snapd = NULL; /* Data structure of snapd access */ - GError *error = NULL; /* Glib error */ - SnapdSnap *snap = NULL; /* Data structure of client Snap */ - GPtrArray *plugs = NULL; /* Plug search result of client Snap */ -# endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */ -# endif /* SUPPORT_SNAPPED_CUPSD */ - + return (HTTP_FORBIDDEN); + } -# ifndef SUPPORT_SNAPPED_CUPSD + best = con->best; - /* If AppArmor is not enabled, then we can't identify the client */ - /* With cupsd running in a Snap, the "mount-observe" interface - needs to be plugged, therefore we do this only if not snapped. */ - if (!aa_is_enabled()) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: No AppArmor in use"); - goto snap_check_done; - } + if ((type = best->type) == CUPSD_AUTH_DEFAULT) + type = cupsdDefaultAuthType(); -# endif /* !SUPPORT_SNAPPED_CUPSD */ + 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)); - if (aa_getpeercon(peerfd, &context, NULL) < 0) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: AppArmor profile could not be retrieved for client process - Error: %s", strerror(errno)); - goto snap_check_done; - } else - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: AppArmor profile of client process: %s", context); + if (best->limit == CUPSD_AUTH_LIMIT_IPP) + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: op=%x(%s)", best->op, ippOpString(best->op)); -# ifdef OUR_SNAP_NAME - /* Is the client one of the utilities of our Snap? */ - if (!strncmp(context, "snap." OUR_SNAP_NAME ".", strlen(OUR_SNAP_NAME) + 6)) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap is the same Snap we are running in, access granted"); - goto snap_check_done; - } -# endif /* OUR_SNAP_NAME */ + /* + * Check host/ip-based accesses... + */ -# ifdef SUPPORT_SNAPPED_CUPSD +#ifdef AF_INET6 + if (httpAddrFamily(hostaddr) == AF_INET6) + { + /* + * Copy IPv6 address... + */ - /* - * Run - * - * snapctl is-connected --apparmor-label AA_CONTEXT CUPS_CONTROL_SLOT - * - * or an equivalent library function call using the - * snapd_client_run_snapctl2_sync() function of libsnapd-glib. - * - * Here AA_CONTEXT is the AppArmor profile name of the client, or - * "unconfined" for an unconfined client and CUPS_CONTROL_SLOT - * the name of the slot of the CUPS Snap to which clients plug - * with their cups-control plug in order to do administrative - * CUPS tasks. - * - * The exit status of the command/function call tells which type - * of client we have to do with: - * - * 0: The client is a confined Snap and plugs cups-control - * -> Grant access - * 1: The client is a confined Snap and does not plug cups-control - * -> Deny access - * 10: The client is a classic Snap - * -> Grant access - * 11: The client is not a Snap - * -> Grant access - * - * NOTE: This method only works if cupsd is running in a Snap - * providing a slot for the client's "cups-control" - * plug. Do not build CUPS with this method when intending - * to use it unsnapped, for example in a Debian or RPM - * package, or directly installed into your system. The - * errors of snapctl missing or snapctl/the function - * running without a Snap context will deny all - * administrative accesses! - * - * When running inside a Snap this method is preferred, as it does not - * require full access to the snapd under which cupsd is running. - */ + address[0] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[0]); + address[1] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[1]); + address[2] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[2]); + address[3] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[3]); + } + else +#endif /* AF_INET6 */ + if (con->http->hostaddr->addr.sa_family == AF_INET) + { + /* + * Copy IPv4 address... + */ -# if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC) + address[0] = 0; + address[1] = 0; + address[2] = 0; + address[3] = ntohl(hostaddr->ipv4.sin_addr.s_addr); + } + else + memset(address, 0, sizeof(address)); - /* - * Use the snapd_client_run_snapctl2_sync() function of libsnapd-glib - */ + hostlen = strlen(hostname); - /* Insert client Snap context in snapctl arguments */ - args[2] = context; + auth = cupsdCheckAccess(address, hostname, hostlen, best) + ? CUPSD_AUTH_ALLOW : CUPSD_AUTH_DENY; - /* Connect to snapd */ - client = snapd_client_new(); - if (!client) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not connect to snapd, permission denied"); - ret = 0; - goto snap_check_done; - } + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: auth=CUPSD_AUTH_%s...", auth ? "DENY" : "ALLOW"); - /* snapctl commands are sent over a this socket that is made - available within the snap sandbox */ - snapd_client_set_socket_path(client, "/run/snapd-snap.socket"); + if (auth == CUPSD_AUTH_DENY && best->satisfy == CUPSD_AUTH_SATISFY_ALL) + return (HTTP_FORBIDDEN); - /* Take cookie from the environment if available */ - cookie = g_getenv("SNAP_COOKIE"); - if (!cookie) - { - cookie = ""; - cupsdLogMessage(CUPSD_LOG_WARN, "cupsdCheckAdminTask: No SNAP_COOKIE set in the Snap environment, client Snap context check may not work"); - } +#ifdef HAVE_TLS + /* + * See if encryption is required... + */ - /* Do the client Snap context check */ - if (!snapd_client_run_snapctl2_sync(client, cookie, args, NULL, NULL, &status, NULL, &error)) { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap context check error - %s", error->message); - ret = 0; - goto snap_check_done; - } + if ((best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http->tls && + _cups_strcasecmp(hostname, "localhost") && + !httpAddrLocalhost(hostaddr) && + best->satisfy == CUPSD_AUTH_SATISFY_ALL) && + !(type == CUPSD_AUTH_NEGOTIATE || + (type == CUPSD_AUTH_NONE && + cupsdDefaultAuthType() == CUPSD_AUTH_NEGOTIATE))) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "cupsdIsAuthorized: Need upgrade to TLS..."); + return (HTTP_UPGRADE_REQUIRED); + } +#endif /* HAVE_TLS */ -# else -# ifdef HAVE_SNAPCTL_IS_CONNECTED + /* + * Now see what access level is required... + */ - /* - * Call the snapctl executable using execv() in a fork - */ + if (best->level == CUPSD_AUTH_ANON || /* Anonymous access - allow it */ + (type == CUPSD_AUTH_NONE && cupsArrayCount(best->names) == 0)) + return (HTTP_OK); - /* Insert client Snap context in snapctl command line */ - args[3] = context; + if (!con->username[0] && type == CUPSD_AUTH_NONE && + best->limit == CUPSD_AUTH_LIMIT_IPP) + { + /* + * Check for unauthenticated username... + */ - /* Create a pipe to catch stderr output from snapctl */ - if (pipe(fds)) - { - fds[0] = -1; - fds[1] = -1; - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to establish stderr pipe for snapctl call - %s", strerror(errno)); - ret = 0; - goto snap_check_done; - } + ipp_attribute_t *attr; /* requesting-user-name attribute */ - /* Set the "close on exec" flag on each end of the pipe... */ - if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC)) - { - close(fds[0]); - close(fds[1]); - fds[0] = -1; - fds[1] = -1; - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to set \"close on exec\" flag on read end of the stderr pipe for snapctl call - %s", strerror(errno)); - ret = 0; - goto snap_check_done; - } - if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC)) - { - close(fds[0]); - close(fds[1]); - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to set \"close on exec\" flag on write end of the stderr pipe for snapctl call - %s", strerror(errno)); - ret = 0; - goto snap_check_done; - } - if ((pid = fork()) == 0) - { - /* Couple pipe with stderr of Ghostscript process */ - if (fds[1] >= 0) { - if (fds[1] != 2) { - if (dup2(fds[1], 2) < 0) { - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to couple pipe with stderr of snapctl process - %s", strerror(errno)); - exit(100); - } - close(fds[1]); - } - close(fds[0]); - } else { - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Invalid pipe file descriptor to couple with stderr of snapctl process - %s", strerror(errno)); - exit(100); - } + attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME); + if (attr) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, + "cupsdIsAuthorized: requesting-user-name=\"%s\"", + attr->values[0].string.text); + strlcpy(username, attr->values[0].string.text, sizeof(username)); + } + else if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY) + return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */ + else + return (HTTP_OK); /* unless overridden with Satisfy */ + } + else + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: username=\"%s\"", + con->username); - /* Send snapctl's stdout to the Nirwana, as snapctl is supposed to - not output anything here */ - if ((nullfd = open("/dev/null", O_RDWR)) > 2) - { - dup2(nullfd, 1); - close(nullfd); - } - else - close(nullfd); - fcntl(1, F_SETFL, O_NDELAY); +#ifdef HAVE_AUTHORIZATION_H + if (!con->username[0] && !con->authref) +#else + if (!con->username[0]) +#endif /* HAVE_AUTHORIZATION_H */ + { + if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY) + return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */ + else + return (HTTP_OK); /* unless overridden with Satisfy */ + } - /* Execute snapctl command line ... */ - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Running command: " SNAPCTL " is-connected --apparmor-label %s " CUPS_CONTROL_SLOT, context); - execv(SNAPCTL, args); - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to launch snapctl: %s", strerror(errno)); - exit(100); - } - else if (pid < 0) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Unable to fork for snapctl call - %s", strerror(errno)); - ret = 0; - goto snap_check_done; - } - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Started snapctl (PID %d)", pid); - close(fds[1]); + if (con->type != type && type != CUPSD_AUTH_NONE && +#ifdef HAVE_GSSAPI + (type != CUPSD_AUTH_NEGOTIATE || con->gss_uid <= 0) && +#endif /* HAVE_GSSAPI */ + con->type != CUPSD_AUTH_BASIC) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "Authorized using %s, expected %s.", + types[con->type], types[type]); - /* Read out stderr from snapctl */ - buf[0] = '\0'; - snapctl_stderr = cupsFileOpenFd(fds[0], "r"); - if (snapctl_stderr) - { - while (cupsFileGets(snapctl_stderr, buf, sizeof(buf)) && buf[0]) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: Error message from snapctl: %s", buf); - ret = 0; - } - cupsFileClose(snapctl_stderr); - } - close(fds[0]); + return (HTTP_UNAUTHORIZED); + } - /* Wait for snapctl to finish */ - retry_wait: - if (waitpid (pid, &wstatus, 0) == -1) - { - if (errno == EINTR) - goto retry_wait; - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl (PID %d) stopped with an error - %s", pid, strerror(errno)); - ret = 0; - goto snap_check_done; - } - if (ret == 0) - goto snap_check_done; - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: snapctl (PID %d) exited with no errors.", pid); + strlcpy(username, con->username, sizeof(username)); + } - /* How did snapctl terminate */ - if (WIFEXITED(wstatus)) - { - /* Via regular exit */ - status = WEXITSTATUS(wstatus); - if (status == 100) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl not executed"); - ret = 0; - goto snap_check_done; - } - } - else if (WIFSIGNALED(wstatus)) - { - /* Via signal */ - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl caught the signal %d", WTERMSIG(wstatus)); - ret = 0; - goto snap_check_done; - } + /* + * OK, got a username. See if we need normal user access, or group + * access... + */ -# endif /* HAVE_SNAPCTL_IS_CONNECTED */ -# endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */ + /* + * Strip any @domain or @KDC from the username and owner... + */ - switch (status) - { - case 0 : - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap connecting via \"cups-control\" interface, access granted"); - break; - case 1 : - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap does not connect via \"cups-control\" interface, permission denied"); - ret = 0; - break; - case 10 : - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap under classic confinement, access granted"); - break; - case 11 : - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client is not a Snap, access granted"); - break; - default : - cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCheckAdminTask: snapctl exited with unknown status: %d", status); - ret = 0; - break; - } + if ((ptr = strchr(username, '@')) != NULL) + *ptr = '\0'; - snap_check_done: - if (context) - free(context); -# if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC) - g_clear_object(&client); -# endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */ + if (owner) + { + strlcpy(ownername, owner, sizeof(ownername)); -# else -# if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB) + if ((ptr = strchr(ownername, '@')) != NULL) + *ptr = '\0'; + } + else + ownername[0] = '\0'; - /* - * If the client is a Snap, extract the client Snap's name from - * the AppArmor context and then query snapd to find out about the - * Snap's confinement type and whether it plugs cups-control. Grant - * access if - * - * - the client is not a Snap - * - the client is a classic Snap - * - the client is a confined Snap plugging "cups-control" - * - * We deny access if - * - * - the client is a confined Snap not plugging "cups-control" - * - an error occurs during the steps of this method - * - * NOTE: This method is only for use of cupsd when it is not - * packaged in a Snap. In a Snap one would need to plug the - * snapd-control interface, which gives full control on - * snapd, a high security risk. Therefore one will not get - * automatic connection of this interface granted in the - * Snap Store. This is the reason why the snapctl method - * (above) got created by the snapd developers. - * - * This is the preferred method to run CUPS unsnapped, as this is - * the only way to check Snap status on clients from an unsnapped - * cupsd. - */ + /* + * Get the user info... + */ - /* If the AppArmor context does not begin with "snap.", then this - is not a snap */ - if (strncmp(context, "snap.", 5) != 0) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: AppArmor context not from a Snap"); - goto snap_check_done; - } + if (username[0]) + { + pw = getpwnam(username); + endpwent(); + } + else + pw = NULL; - dot = strchr(context + 5, '.'); - if (dot == NULL) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Malformed snapd AppArmor profile name: %s", context); - goto snap_check_done; - } - snap_name = strndup(context + 5, (size_t)(dot - context - 5)); - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client is the Snap %s", snap_name); + /* + * For matching user and group memberships below we will first go + * through all names except @SYSTEM to authorize the task as + * non-administrative, like printing or deleting one's own job, if this + * fails we will check whether we can authorize via the special name + * @SYSTEM, as an administrative task, like creating a print queue or + * deleting someone else's job. + * Note that tasks are considered as administrative by the policies + * in cupsd.conf, when they require the user or group @SYSTEM. + * We do this separation because if the client is a Snap connecting via + * domain socket, we need to additionally check whether it plugs to us + * through the "cups-control" interface which allows administration and + * not through the "cups" interface which allows only printing. + */ - /* Connect to snapd */ - snapd = snapd_client_new(); - if (!snapd) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not connect to snapd, permission denied"); - ret = 0; - goto snap_check_done; - } + if (best->level == CUPSD_AUTH_USER) + { + /* + * If there are no names associated with this location, then + * any valid user is OK... + */ - /* Check whether the client Snap is under classic confinement */ - snap = snapd_client_get_snap_sync(snapd, snap_name, NULL, &error); - if (!snap) + if (cupsArrayCount(best->names) == 0) + return (HTTP_OK); + + /* + * Otherwise check the user list and return OK if this user is + * allowed... + */ + + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership..."); + +#ifdef HAVE_AUTHORIZATION_H + /* + * If an authorization reference was supplied it must match a right name... + */ + + if (con->authref) + { + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not obtain client Snap data: \"%s\", permission denied", error->message); - ret = 0; - goto snap_check_done; + if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9)) + return (HTTP_OK); } - /* Snaps using classic confinement are granted access */ - if (snapd_snap_get_confinement(snap) == SNAPD_CONFINEMENT_CLASSIC) + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap under classic confinement, access granted"); - goto snap_check_done; + if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey && + check_authref(con, SystemGroupAuthKey)) + return (HTTP_OK); } - /* Get list of interfaces to which the client Snap is plugging */ - if (!snapd_client_get_connections2_sync(snapd, SNAPD_GET_CONNECTIONS_FLAGS_NONE, snap_name, "cups-control", NULL, NULL, &plugs, NULL, NULL, &error)) + return (HTTP_FORBIDDEN); + } +#endif /* HAVE_AUTHORIZATION_H */ + + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) + { + if (!_cups_strcasecmp(name, "@OWNER") && owner && + !_cups_strcasecmp(username, ownername)) + return (HTTP_OK); + else if (!_cups_strcasecmp(name, "@SYSTEM")) { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Could not obtain the client Snap's interface connections: \"%s\", permission denied", error->message); - ret = 0; - goto snap_check_done; + /* Do @SYSTEM later, when every other entry fails */ + continue; } - - if (plugs->len <= 0) + else if (name[0] == '@') { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap does not connect via \"cups-control\" interface, permission denied"); - ret = 0; - goto snap_check_done; + if (cupsdCheckGroup(username, pw, name + 1)) + return (HTTP_OK); } + else if (!_cups_strcasecmp(username, name)) + return (HTTP_OK); + } - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Client Snap connecting via \"cups-control\" interface, access granted"); - - snap_check_done: - if (context) - free(context); - if (snap_name) - free(snap_name); - g_clear_object(&snapd); - g_clear_object(&snap); - if (plugs) - g_ptr_array_unref(plugs); + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) + { + if (!_cups_strcasecmp(name, "@SYSTEM")) + { + for (i = 0; i < NumSystemGroups; i ++) + if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_task(con)) + return (HTTP_OK); + } + } -# endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */ -# endif /* SUPPORT_SNAPPED_CUPSD */ + return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED); + } -# ifndef CHECK_METHOD_FOUND + /* + * Check to see if this user is in any of the named groups... + */ - /* - * Issue warning if requirements for building Snap-related access control - * not fulfilled - */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership..."); - cupsdLogMessage(CUPSD_LOG_WARN, "cupsdCheckAdminTask: Compiling problem: none of the three access control methods (libsnapd-glib snapd access, \"snapctl is-connected\", libsnapd-glib-based snapctl call) available, no Snap-related access control built!"); + /* + * Check to see if this user is in any of the named groups... + */ - snap_check_done: - if (context) - free(context); + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) + { + if (!_cups_strcasecmp(name, "@SYSTEM")) + { + /* Do @SYSTEM later, when every other entry fails */ + continue; + } -# endif /* !CHECK_METHOD_FOUND */ + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name); - } + if (cupsdCheckGroup(username, pw, name)) + return (HTTP_OK); } - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Access %s", ret == 1 ? "granted" : "denied"); + for (name = (char *)cupsArrayFirst(best->names); + name; + name = (char *)cupsArrayNext(best->names)) + { + if (!_cups_strcasecmp(name, "@SYSTEM")) + { + cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name); -#else + for (i = 0; i < NumSystemGroups; i ++) + if (cupsdCheckGroup(username, pw, SystemGroups[i]) && check_admin_task(con)) + return (HTTP_OK); + } + } - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdCheckAdminTask: Access granted (no extra checking)"); + /* + * The user isn't part of the specified group, so deny access... + */ -#endif /* AF_LOCAL && SUPPORT_SNAPPED_CLIENTS */ + cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: User not in group(s)."); - return ret; + return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED); } /* - * 'cupsdIsAuthorized()' - Check to see if the user is authorized... + * 'cupsdNewLocation()' - Create a new location for authorization. + * + * Note: Still need to call cupsdAddLocation() to add it to the list of global + * locations. */ -http_status_t /* O - HTTP_OK if authorized or error code */ -cupsdIsAuthorized(cupsd_client_t *con, /* I - Connection */ - const char *owner)/* I - Owner of object */ +cupsd_location_t * /* O - Pointer to new location record */ +cupsdNewLocation(const char *location) /* I - Location path */ { - int i, /* Looping vars */ - auth, /* Authorization status */ - type; /* Type of authentication */ - http_addr_t *hostaddr = httpGetAddress(con->http); - /* Client address */ - const char *hostname = httpGetHostname(con->http, NULL, 0); - /* Client hostname */ - unsigned address[4]; /* Authorization address */ - cupsd_location_t *best; /* Best match for location so far */ - size_t hostlen; /* Length of hostname */ - char *name, /* Current username */ - username[256], /* Username to authorize */ - ownername[256], /* Owner name to authorize */ - *ptr; /* Pointer into username */ - struct passwd *pw; /* User password data */ - static const char * const levels[] = /* Auth levels */ - { - "ANON", - "USER", - "GROUP" - }; - static const char * const types[] = /* Auth types */ - { - "None", - "Basic", - "Negotiate" - }; + cupsd_location_t *temp; /* New location */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", con->uri, con->best, con->best ? con->best->location ? con->best->location : "(null)" : ""); - if (owner) - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: owner=\"%s\"", owner); + /* + * Try to allocate memory for the new location. + */ + + if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL) + return (NULL); /* - * If there is no "best" authentication rule for this request, then - * access is allowed from the local system and denied from other - * addresses... + * Initialize the record and copy the name over... */ - if (!con->best) + if ((temp->location = _cupsStrAlloc(location)) == NULL) { - if (httpAddrLocalhost(httpGetAddress(con->http)) || - !strcmp(hostname, ServerName) || - cupsArrayFind(ServerAlias, (void *)hostname)) - return (HTTP_OK); - else - return (HTTP_FORBIDDEN); + free(temp); + return (NULL); } - best = con->best; + temp->length = strlen(temp->location); - if ((type = best->type) == CUPSD_AUTH_DEFAULT) - type = cupsdDefaultAuthType(); + /* + * Return the new record... + */ - 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)); + return (temp); +} + + +/* + * 'check_admin_task()' - Do additional checks on administrative tasks + */ + +static int /* O - 1 if admin task authorized */ +check_admin_task(cupsd_client_t *con) /* I - Connection */ +{ + int ret = 1; /* Return value */ - if (best->limit == CUPSD_AUTH_LIMIT_IPP) - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: op=%x(%s)", best->op, ippOpString(best->op)); + + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Administrative task"); /* - * Check host/ip-based accesses... + * If the client accesses locally via domain socket, find out whether it + * is a Snap. Grant access if it is not a Snap, if it is a classic Snap + * or if it is a confined Snap which plugs "cups-control", deny access + * if it is a confined Snap not plugging "cups-control" or if an error + * occurs in the process of finding this out. */ -#ifdef AF_INET6 - if (httpAddrFamily(hostaddr) == AF_INET6) - { - /* - * Copy IPv6 address... - */ +#if defined(AF_LOCAL) && defined(SUPPORT_SNAPPED_CLIENTS) - address[0] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[0]); - address[1] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[1]); - address[2] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[2]); - address[3] = ntohl(hostaddr->ipv6.sin6_addr.s6_addr32[3]); - } - else -#endif /* AF_INET6 */ - if (con->http->hostaddr->addr.sa_family == AF_INET) + /* + * Get the client's file descriptor and from this its AppArmor context + */ + + if (httpAddrFamily(con->http->hostaddr) == AF_LOCAL) { - /* - * Copy IPv4 address... - */ + int peerfd; /* Peer's file descriptor */ - address[0] = 0; - address[1] = 0; - address[2] = 0; - address[3] = ntohl(hostaddr->ipv4.sin_addr.s_addr); - } - else - memset(address, 0, sizeof(address)); + peerfd = httpGetFd(con->http); - hostlen = strlen(hostname); + if (peerfd < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to get peer file descriptor of client connecting via domain socket"); + } + else + { + char *context = NULL; /* AppArmor profile name of client */ +# undef CHECK_METHOD_FOUND +# ifdef SUPPORT_SNAPPED_CUPSD + int status = 65535; /* Status of client Snap context check */ +# if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC) +# define CHECK_METHOD_FOUND 1 + char *args[] = { "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl arguments */ + SnapdClient *client = NULL; /* Data structure of snapd access */ + const char *cookie; /* snapd access cookie */ + GError *error = NULL; /* Glib error */ +# else +# ifdef HAVE_SNAPCTL_IS_CONNECTED +# define CHECK_METHOD_FOUND 1 + char *args[] = { SNAPCTL, "is-connected", "--apparmor-label", NULL, CUPS_CONTROL_SLOT, NULL }; /* snapctl command line */ + int fds[2], /* Pipe file descriptors for stderr of + snapctl */ + nullfd; /* /dev/null file descriptor for stdout of + snapctl */ + pid_t pid; /* PID of snapctl */ + cups_file_t *snapctl_stderr; /* CUPS FP for stderr of snapctl */ + char buf[1024]; /* Buffer for snapctl's stderr output */ + int wstatus; /* Wait result of forked snapctl process */ +# endif /* HAVE_SNAPCTL_IS_CONNECTED */ +# endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */ +# else +# if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB) +# define CHECK_METHOD_FOUND 1 + char *snap_name = NULL; /* Client Snap name */ + char *dot; /* Pointer to dot in AppArmor profile name */ + SnapdClient *snapd = NULL; /* Data structure of snapd access */ + GError *error = NULL; /* Glib error */ + SnapdSnap *snap = NULL; /* Data structure of client Snap */ + GPtrArray *plugs = NULL; /* Plug search result of client Snap */ +# endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */ +# endif /* SUPPORT_SNAPPED_CUPSD */ - auth = cupsdCheckAccess(address, hostname, hostlen, best) - ? CUPSD_AUTH_ALLOW : CUPSD_AUTH_DENY; - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: auth=CUPSD_AUTH_%s...", auth ? "DENY" : "ALLOW"); +# ifndef SUPPORT_SNAPPED_CUPSD - if (auth == CUPSD_AUTH_DENY && best->satisfy == CUPSD_AUTH_SATISFY_ALL) - return (HTTP_FORBIDDEN); + /* If AppArmor is not enabled, then we can't identify the client */ + /* With cupsd running in a Snap, the "mount-observe" interface + needs to be plugged, therefore we do this only if not snapped. */ + if (!aa_is_enabled()) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: No AppArmor in use"); + goto snap_check_done; + } -#ifdef HAVE_TLS - /* - * See if encryption is required... - */ +# endif /* !SUPPORT_SNAPPED_CUPSD */ + + if (aa_getpeercon(peerfd, &context, NULL) < 0) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: AppArmor profile could not be retrieved for client process - Error: %s", strerror(errno)); + goto snap_check_done; + } else + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: AppArmor profile of client process: %s", context); + +# ifdef OUR_SNAP_NAME + /* Is the client one of the utilities of our Snap? */ + if (!strncmp(context, "snap." OUR_SNAP_NAME ".", strlen(OUR_SNAP_NAME) + 6)) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap is the same Snap we are running in, access granted"); + goto snap_check_done; + } +# endif /* OUR_SNAP_NAME */ + +# ifdef SUPPORT_SNAPPED_CUPSD + + /* + * Run + * + * snapctl is-connected --apparmor-label AA_CONTEXT CUPS_CONTROL_SLOT + * + * or an equivalent library function call using the + * snapd_client_run_snapctl2_sync() function of libsnapd-glib. + * + * Here AA_CONTEXT is the AppArmor profile name of the client, or + * "unconfined" for an unconfined client and CUPS_CONTROL_SLOT + * the name of the slot of the CUPS Snap to which clients plug + * with their cups-control plug in order to do administrative + * CUPS tasks. + * + * The exit status of the command/function call tells which type + * of client we have to do with: + * + * 0: The client is a confined Snap and plugs cups-control + * -> Grant access + * 1: The client is a confined Snap and does not plug cups-control + * -> Deny access + * 10: The client is a classic Snap + * -> Grant access + * 11: The client is not a Snap + * -> Grant access + * + * NOTE: This method only works if cupsd is running in a Snap + * providing a slot for the client's "cups-control" + * plug. Do not build CUPS with this method when intending + * to use it unsnapped, for example in a Debian or RPM + * package, or directly installed into your system. The + * errors of snapctl missing or snapctl/the function + * running without a Snap context will deny all + * administrative accesses! + * + * When running inside a Snap this method is preferred, as it does not + * require full access to the snapd under which cupsd is running. + */ - if ((best->encryption >= HTTP_ENCRYPT_REQUIRED && !con->http->tls && - _cups_strcasecmp(hostname, "localhost") && - !httpAddrLocalhost(hostaddr) && - best->satisfy == CUPSD_AUTH_SATISFY_ALL) && - !(type == CUPSD_AUTH_NEGOTIATE || - (type == CUPSD_AUTH_NONE && - cupsdDefaultAuthType() == CUPSD_AUTH_NEGOTIATE))) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdIsAuthorized: Need upgrade to TLS..."); - return (HTTP_UPGRADE_REQUIRED); - } -#endif /* HAVE_TLS */ +# if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC) - /* - * Now see what access level is required... - */ + /* + * Use the snapd_client_run_snapctl2_sync() function of libsnapd-glib + */ - if (best->level == CUPSD_AUTH_ANON || /* Anonymous access - allow it */ - (type == CUPSD_AUTH_NONE && cupsArrayCount(best->names) == 0)) - return (HTTP_OK); + /* Insert client Snap context in snapctl arguments */ + args[2] = context; - if (!con->username[0] && type == CUPSD_AUTH_NONE && - best->limit == CUPSD_AUTH_LIMIT_IPP) - { - /* - * Check for unauthenticated username... - */ + /* Connect to snapd */ + client = snapd_client_new(); + if (!client) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not connect to snapd, permission denied"); + ret = 0; + goto snap_check_done; + } - ipp_attribute_t *attr; /* requesting-user-name attribute */ + /* snapctl commands are sent over a this socket that is made + available within the snap sandbox */ + snapd_client_set_socket_path(client, "/run/snapd-snap.socket"); + /* Take cookie from the environment if available */ + cookie = g_getenv("SNAP_COOKIE"); + if (!cookie) + { + cookie = ""; + cupsdLogMessage(CUPSD_LOG_WARN, "check_admin_task: No SNAP_COOKIE set in the Snap environment, client Snap context check may not work"); + } - attr = ippFindAttribute(con->request, "requesting-user-name", IPP_TAG_NAME); - if (attr) - { - cupsdLogMessage(CUPSD_LOG_DEBUG, - "cupsdIsAuthorized: requesting-user-name=\"%s\"", - attr->values[0].string.text); - strlcpy(username, attr->values[0].string.text, sizeof(username)); - } - else if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY) - return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */ - else - return (HTTP_OK); /* unless overridden with Satisfy */ - } - else - { - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: username=\"%s\"", - con->username); + /* Do the client Snap context check */ + if (!snapd_client_run_snapctl2_sync(client, cookie, args, NULL, NULL, &status, NULL, &error)) { + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap context check error - %s", error->message); + ret = 0; + goto snap_check_done; + } -#ifdef HAVE_AUTHORIZATION_H - if (!con->username[0] && !con->authref) -#else - if (!con->username[0]) -#endif /* HAVE_AUTHORIZATION_H */ - { - if (best->satisfy == CUPSD_AUTH_SATISFY_ALL || auth == CUPSD_AUTH_DENY) - return (HTTP_UNAUTHORIZED); /* Non-anonymous needs user/pass */ - else - return (HTTP_OK); /* unless overridden with Satisfy */ - } +# else +# ifdef HAVE_SNAPCTL_IS_CONNECTED + /* + * Call the snapctl executable using execv() in a fork + */ - if (con->type != type && type != CUPSD_AUTH_NONE && -#ifdef HAVE_GSSAPI - (type != CUPSD_AUTH_NEGOTIATE || con->gss_uid <= 0) && -#endif /* HAVE_GSSAPI */ - con->type != CUPSD_AUTH_BASIC) - { - cupsdLogMessage(CUPSD_LOG_ERROR, "Authorized using %s, expected %s.", - types[con->type], types[type]); + /* Insert client Snap context in snapctl command line */ + args[3] = context; - return (HTTP_UNAUTHORIZED); - } + /* Create a pipe to catch stderr output from snapctl */ + if (pipe(fds)) + { + fds[0] = -1; + fds[1] = -1; + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to establish stderr pipe for snapctl call - %s", strerror(errno)); + ret = 0; + goto snap_check_done; + } - strlcpy(username, con->username, sizeof(username)); - } + /* Set the "close on exec" flag on each end of the pipe... */ + if (fcntl(fds[0], F_SETFD, fcntl(fds[0], F_GETFD) | FD_CLOEXEC)) + { + close(fds[0]); + close(fds[1]); + fds[0] = -1; + fds[1] = -1; + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to set \"close on exec\" flag on read end of the stderr pipe for snapctl call - %s", strerror(errno)); + ret = 0; + goto snap_check_done; + } + if (fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC)) + { + close(fds[0]); + close(fds[1]); + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to set \"close on exec\" flag on write end of the stderr pipe for snapctl call - %s", strerror(errno)); + ret = 0; + goto snap_check_done; + } - /* - * OK, got a username. See if we need normal user access, or group - * access... - */ + if ((pid = fork()) == 0) + { + /* Couple pipe with stderr of Ghostscript process */ + if (fds[1] >= 0) { + if (fds[1] != 2) { + if (dup2(fds[1], 2) < 0) { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to couple pipe with stderr of snapctl process - %s", strerror(errno)); + exit(100); + } + close(fds[1]); + } + close(fds[0]); + } else { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Invalid pipe file descriptor to couple with stderr of snapctl process - %s", strerror(errno)); + exit(100); + } - /* - * Strip any @domain or @KDC from the username and owner... - */ + /* Send snapctl's stdout to the Nirwana, as snapctl is supposed to + not output anything here */ + if ((nullfd = open("/dev/null", O_RDWR)) > 2) + { + dup2(nullfd, 1); + close(nullfd); + } + else + close(nullfd); + fcntl(1, F_SETFL, O_NDELAY); - if ((ptr = strchr(username, '@')) != NULL) - *ptr = '\0'; + /* Execute snapctl command line ... */ + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Running command: " SNAPCTL " is-connected --apparmor-label %s " CUPS_CONTROL_SLOT, context); + execv(SNAPCTL, args); + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to launch snapctl: %s", strerror(errno)); + exit(100); + } + else if (pid < 0) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Unable to fork for snapctl call - %s", strerror(errno)); + ret = 0; + goto snap_check_done; + } + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Started snapctl (PID %d)", pid); - if (owner) - { - strlcpy(ownername, owner, sizeof(ownername)); + close(fds[1]); - if ((ptr = strchr(ownername, '@')) != NULL) - *ptr = '\0'; - } - else - ownername[0] = '\0'; + /* Read out stderr from snapctl */ + buf[0] = '\0'; + snapctl_stderr = cupsFileOpenFd(fds[0], "r"); + if (snapctl_stderr) + { + while (cupsFileGets(snapctl_stderr, buf, sizeof(buf)) && buf[0]) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: Error message from snapctl: %s", buf); + ret = 0; + } + cupsFileClose(snapctl_stderr); + } + close(fds[0]); - /* - * Get the user info... - */ + /* Wait for snapctl to finish */ + retry_wait: + if (waitpid (pid, &wstatus, 0) == -1) + { + if (errno == EINTR) + goto retry_wait; + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl (PID %d) stopped with an error - %s", pid, strerror(errno)); + ret = 0; + goto snap_check_done; + } + if (ret == 0) + goto snap_check_done; + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: snapctl (PID %d) exited with no errors.", pid); - if (username[0]) - { - pw = getpwnam(username); - endpwent(); - } - else - pw = NULL; + /* How did snapctl terminate */ + if (WIFEXITED(wstatus)) + { + /* Via regular exit */ + status = WEXITSTATUS(wstatus); + if (status == 100) + { + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl not executed"); + ret = 0; + goto snap_check_done; + } + } + else if (WIFSIGNALED(wstatus)) + { + /* Via signal */ + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl caught the signal %d", WTERMSIG(wstatus)); + ret = 0; + goto snap_check_done; + } - /* - * For matching user and group memberships below we will first go - * through all names except @SYSTEM to authorize the task as - * non-administrative, like printing or deleting one's own job, if this - * fails we will check whether we can authorize via the special name - * @SYSTEM, as an administrative task, like creating a print queue or - * deleting someone else's job. - * Note that tasks are considered as administrative by the policies - * in cupsd.conf, when they require the user or group @SYSTEM. - * We do this separation because if the client is a Snap connecting via - * domain socket, we need to additionally check whether it plugs to us - * through the "cups-control" interface which allows administration and - * not through the "cups" interface which allows only printing. - */ +# endif /* HAVE_SNAPCTL_IS_CONNECTED */ +# endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */ - if (best->level == CUPSD_AUTH_USER) - { - /* - * If there are no names associated with this location, then - * any valid user is OK... - */ + switch (status) + { + case 0 : + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap connecting via \"cups-control\" interface, access granted"); + break; + case 1 : + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap does not connect via \"cups-control\" interface, permission denied"); + ret = 0; + break; + case 10 : + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap under classic confinement, access granted"); + break; + case 11 : + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client is not a Snap, access granted"); + break; + default : + cupsdLogMessage(CUPSD_LOG_ERROR, "check_admin_task: snapctl exited with unknown status: %d", status); + ret = 0; + break; + } - if (cupsArrayCount(best->names) == 0) - return (HTTP_OK); + snap_check_done: + if (context) + free(context); +# if defined(HAVE_SNAPDGLIB) && defined(HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC) + g_clear_object(&client); +# endif /* HAVE_SNAPDGLIB && HAVE_SNAPD_CLIENT_RUN_SNAPCTL2_SYNC */ - /* - * Otherwise check the user list and return OK if this user is - * allowed... - */ +# else +# if !defined(SUPPORT_SNAPPED_CUPSD) && defined(HAVE_SNAPDGLIB) - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking user membership..."); + /* + * If the client is a Snap, extract the client Snap's name from + * the AppArmor context and then query snapd to find out about the + * Snap's confinement type and whether it plugs cups-control. Grant + * access if + * + * - the client is not a Snap + * - the client is a classic Snap + * - the client is a confined Snap plugging "cups-control" + * + * We deny access if + * + * - the client is a confined Snap not plugging "cups-control" + * - an error occurs during the steps of this method + * + * NOTE: This method is only for use of cupsd when it is not + * packaged in a Snap. In a Snap one would need to plug the + * snapd-control interface, which gives full control on + * snapd, a high security risk. Therefore one will not get + * automatic connection of this interface granted in the + * Snap Store. This is the reason why the snapctl method + * (above) got created by the snapd developers. + * + * This is the preferred method to run CUPS unsnapped, as this is + * the only way to check Snap status on clients from an unsnapped + * cupsd. + */ -#ifdef HAVE_AUTHORIZATION_H - /* - * If an authorization reference was supplied it must match a right name... - */ + /* If the AppArmor context does not begin with "snap.", then this + is not a snap */ + if (strncmp(context, "snap.", 5) != 0) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: AppArmor context not from a Snap"); + goto snap_check_done; + } - if (con->authref) - { - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) + dot = strchr(context + 5, '.'); + if (dot == NULL) { - if (!_cups_strncasecmp(name, "@AUTHKEY(", 9) && check_authref(con, name + 9)) - return (HTTP_OK); + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Malformed snapd AppArmor profile name: %s", context); + goto snap_check_done; } + snap_name = strndup(context + 5, (size_t)(dot - context - 5)); + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client is the Snap %s", snap_name); - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) + /* Connect to snapd */ + snapd = snapd_client_new(); + if (!snapd) { - if (!_cups_strcasecmp(name, "@SYSTEM") && SystemGroupAuthKey && - check_authref(con, SystemGroupAuthKey) && - cupsdCheckAdminTask(con)) - return (HTTP_OK); + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not connect to snapd, permission denied"); + ret = 0; + goto snap_check_done; } - return (HTTP_FORBIDDEN); - } -#endif /* HAVE_AUTHORIZATION_H */ + /* Check whether the client Snap is under classic confinement */ + snap = snapd_client_get_snap_sync(snapd, snap_name, NULL, &error); + if (!snap) + { + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not obtain client Snap data: \"%s\", permission denied", error->message); + ret = 0; + goto snap_check_done; + } - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) - { - if (!_cups_strcasecmp(name, "@OWNER") && owner && - !_cups_strcasecmp(username, ownername)) - return (HTTP_OK); - else if (!_cups_strcasecmp(name, "@SYSTEM")) + /* Snaps using classic confinement are granted access */ + if (snapd_snap_get_confinement(snap) == SNAPD_CONFINEMENT_CLASSIC) { - /* Do @SYSTEM later, when every other entry fails */ - continue; + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap under classic confinement, access granted"); + goto snap_check_done; } - else if (name[0] == '@') + + /* Get list of interfaces to which the client Snap is plugging */ + if (!snapd_client_get_connections2_sync(snapd, SNAPD_GET_CONNECTIONS_FLAGS_NONE, snap_name, "cups-control", NULL, NULL, &plugs, NULL, NULL, &error)) { - if (cupsdCheckGroup(username, pw, name + 1)) - return (HTTP_OK); + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Could not obtain the client Snap's interface connections: \"%s\", permission denied", error->message); + ret = 0; + goto snap_check_done; } - else if (!_cups_strcasecmp(username, name)) - return (HTTP_OK); - } - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) - { - if (!_cups_strcasecmp(name, "@SYSTEM")) + if (plugs->len <= 0) { - for (i = 0; i < NumSystemGroups; i ++) - if (cupsdCheckGroup(username, pw, SystemGroups[i]) && - cupsdCheckAdminTask(con)) - return (HTTP_OK); + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap does not connect via \"cups-control\" interface, permission denied"); + ret = 0; + goto snap_check_done; } - } - return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED); - } + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Client Snap connecting via \"cups-control\" interface, access granted"); - /* - * Check to see if this user is in any of the named groups... - */ + snap_check_done: + if (context) + free(context); + if (snap_name) + free(snap_name); + g_clear_object(&snapd); + g_clear_object(&snap); + if (plugs) + g_ptr_array_unref(plugs); - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group membership..."); +# endif /* !SUPPORT_SNAPPED_CUPSD && HAVE_SNAPDGLIB */ +# endif /* SUPPORT_SNAPPED_CUPSD */ - /* - * Check to see if this user is in any of the named groups... - */ +# ifndef CHECK_METHOD_FOUND - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) - { - if (!_cups_strcasecmp(name, "@SYSTEM")) - { - /* Do @SYSTEM later, when every other entry fails */ - continue; - } + /* + * Issue warning if requirements for building Snap-related access control + * not fulfilled + */ - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name); + cupsdLogMessage(CUPSD_LOG_WARN, "check_admin_task: Compiling problem: none of the three access control methods (libsnapd-glib snapd access, \"snapctl is-connected\", libsnapd-glib-based snapctl call) available, no Snap-related access control built!"); - if (cupsdCheckGroup(username, pw, name)) - return (HTTP_OK); - } + snap_check_done: + if (context) + free(context); - for (name = (char *)cupsArrayFirst(best->names); - name; - name = (char *)cupsArrayNext(best->names)) - { - if (!_cups_strcasecmp(name, "@SYSTEM")) - { - cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: Checking group \"%s\" membership...", name); +# endif /* !CHECK_METHOD_FOUND */ - for (i = 0; i < NumSystemGroups; i ++) - if (cupsdCheckGroup(username, pw, SystemGroups[i]) && - cupsdCheckAdminTask(con)) - return (HTTP_OK); } } - /* - * The user isn't part of the specified group, so deny access... - */ - - cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdIsAuthorized: User not in group(s)."); - - return (con->username[0] ? HTTP_FORBIDDEN : HTTP_UNAUTHORIZED); -} - - -/* - * 'cupsdNewLocation()' - Create a new location for authorization. - * - * Note: Still need to call cupsdAddLocation() to add it to the list of global - * locations. - */ - -cupsd_location_t * /* O - Pointer to new location record */ -cupsdNewLocation(const char *location) /* I - Location path */ -{ - cupsd_location_t *temp; /* New location */ - - - /* - * Try to allocate memory for the new location. - */ - - if ((temp = calloc(1, sizeof(cupsd_location_t))) == NULL) - return (NULL); - - /* - * Initialize the record and copy the name over... - */ + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Access %s", ret == 1 ? "granted" : "denied"); - if ((temp->location = _cupsStrAlloc(location)) == NULL) - { - free(temp); - return (NULL); - } +#else - temp->length = strlen(temp->location); + (void)con; + cupsdLogMessage(CUPSD_LOG_DEBUG, "check_admin_task: Access granted (no extra checking)"); - /* - * Return the new record... - */ +#endif /* AF_LOCAL && SUPPORT_SNAPPED_CLIENTS */ - return (temp); + return ret; } diff --git a/scheduler/cupsfilter.c b/scheduler/cupsfilter.c index 23993aaf93..d9192b4242 100644 --- a/scheduler/cupsfilter.c +++ b/scheduler/cupsfilter.c @@ -373,7 +373,6 @@ main(int argc, /* I - Number of command-line args */ default : /* Something we don't understand... */ _cupsLangPrintf(stderr, _("%s: Error - unknown option \"-%c\"."), argv[0], *opt); usage(NULL); - break; } } } diff --git a/scheduler/ipp.c b/scheduler/ipp.c index 189079f920..800ccff231 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -2207,7 +2207,7 @@ add_printer(cupsd_client_t *con, /* I - Client connection */ ipp_attribute_t *uri) /* I - URI of printer */ { http_status_t status; /* Policy status */ - int i; /* Looping var */ + int i = 0; /* Looping var */ char scheme[HTTP_MAX_URI], /* Method portion of URI */ username[HTTP_MAX_URI], /* Username portion of URI */ host[HTTP_MAX_URI], /* Host portion of URI */ diff --git a/systemv/cupsctl.c b/systemv/cupsctl.c index 3b5b4c58c4..4568fb9497 100644 --- a/systemv/cupsctl.c +++ b/systemv/cupsctl.c @@ -151,7 +151,6 @@ main(int argc, /* I - Number of command-line args */ default : usage(opt); - break; } } } diff --git a/tools/ippeveps.c b/tools/ippeveps.c index cdedc8899a..28631beecc 100644 --- a/tools/ippeveps.c +++ b/tools/ippeveps.c @@ -417,10 +417,8 @@ get_options(cups_option_t **options) /* O - Options */ * Load PPD file and the corresponding IPP <-> PPD cache data... */ - if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL) + if ((ppd = ppdOpenFile(getenv("PPD"))) != NULL && (ppd_cache = _ppdCacheCreateWithPPD(ppd)) != NULL) { - ppd_cache = _ppdCacheCreateWithPPD(ppd); - /* TODO: Fix me - values are names, not numbers... Also need to support finishings-col */ if ((value = getenv("IPP_FINISHINGS")) == NULL) value = getenv("IPP_FINISHINGS_DEFAULT"); diff --git a/tools/ipptool.c b/tools/ipptool.c index e89fb5bcad..7c8bc6d96e 100644 --- a/tools/ipptool.c +++ b/tools/ipptool.c @@ -1645,36 +1645,38 @@ do_test(_ipp_file_t *f, /* I - IPP data file */ status_ok = 1; repeat_test = 1; } - - for (i = 0, status_ok = 0; i < data->num_statuses; i ++) + else { - if (data->statuses[i].if_defined && - !_ippVarsGet(data->vars, data->statuses[i].if_defined)) - continue; - - if (data->statuses[i].if_not_defined && - _ippVarsGet(data->vars, data->statuses[i].if_not_defined)) - continue; - - if (ippGetStatusCode(response) == data->statuses[i].status) + for (i = 0, status_ok = 0; i < data->num_statuses; i ++) { - status_ok = 1; - - if (data->statuses[i].repeat_match && repeat_count < data->statuses[i].repeat_limit) - repeat_test = 1; + if (data->statuses[i].if_defined && + !_ippVarsGet(data->vars, data->statuses[i].if_defined)) + continue; - if (data->statuses[i].define_match) - _ippVarsSet(data->vars, data->statuses[i].define_match, "1"); - } - else - { - if (data->statuses[i].repeat_no_match && repeat_count < data->statuses[i].repeat_limit) - repeat_test = 1; + if (data->statuses[i].if_not_defined && + _ippVarsGet(data->vars, data->statuses[i].if_not_defined)) + continue; - if (data->statuses[i].define_no_match) + if (ippGetStatusCode(response) == data->statuses[i].status) { - _ippVarsSet(data->vars, data->statuses[i].define_no_match, "1"); status_ok = 1; + + if (data->statuses[i].repeat_match && repeat_count < data->statuses[i].repeat_limit) + repeat_test = 1; + + if (data->statuses[i].define_match) + _ippVarsSet(data->vars, data->statuses[i].define_match, "1"); + } + else + { + if (data->statuses[i].repeat_no_match && repeat_count < data->statuses[i].repeat_limit) + repeat_test = 1; + + if (data->statuses[i].define_no_match) + { + _ippVarsSet(data->vars, data->statuses[i].define_no_match, "1"); + status_ok = 1; + } } } } diff --git a/xcode/CUPS.xcodeproj/project.pbxproj b/xcode/CUPS.xcodeproj/project.pbxproj index 253289e8fe..f4311664ae 100644 --- a/xcode/CUPS.xcodeproj/project.pbxproj +++ b/xcode/CUPS.xcodeproj/project.pbxproj @@ -901,12 +901,12 @@ 72D53A3015B4923F003F877F /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E5136B64AF00836530 /* CoreFoundation.framework */; }; 72D53A3115B4923F003F877F /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E7136B64B000836530 /* Security.framework */; }; 72D53A3215B4923F003F877F /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E8136B64B000836530 /* SystemConfiguration.framework */; }; - 72D53A3415B4925B003F877F /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A3315B4925B003F877F /* ApplicationServices.framework */; }; + 72D53A3415B4925B003F877F /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A3315B4925B003F877F /* ApplicationServices.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 72D53A3515B49270003F877F /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A2C15B4913D003F877F /* IOKit.framework */; }; 72D53A3815B4929D003F877F /* colorman.c in Sources */ = {isa = PBXBuildFile; fileRef = 72D53A3615B4929D003F877F /* colorman.c */; }; 72D53A3A15B492FA003F877F /* libpam.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A3915B492FA003F877F /* libpam.dylib */; }; - 72D53A3B15B4930A003F877F /* GSS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A2915B49110003F877F /* GSS.framework */; }; - 72D53A3C15B4930A003F877F /* Kerberos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E6136B64B000836530 /* Kerberos.framework */; }; + 72D53A3B15B4930A003F877F /* GSS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72D53A2915B49110003F877F /* GSS.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; + 72D53A3C15B4930A003F877F /* Kerberos.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 278C58E6136B64B000836530 /* Kerberos.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 72F75A5C1336F988004BB496 /* cupstestppd.c in Sources */ = {isa = PBXBuildFile; fileRef = 72F75A5B1336F988004BB496 /* cupstestppd.c */; }; 72F75A671336FA38004BB496 /* libcups.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 72220EAE1333047D00FCA411 /* libcups.dylib */; }; /* End PBXBuildFile section */