root/usr/src/cmd/lp/cmd/lpsched/validate.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright (c) 2016 by Delphix. All rights reserved.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */


/* SVr4.0 1.11.1.10     */
/* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */

#include "lpsched.h"

#include "validate.h"

#include <syslog.h>
#include <errno.h>
#include <deflt.h>
#include <tsol/label.h>
#include <auth_list.h>

#define register auto


int             pickfilter ( RSTATUS * , CANDIDATE * , FSTATUS * );

unsigned long           chkprinter_result       = 0;
char *                  o_cpi           = 0;
char *                  o_lpi           = 0;
char *                  o_width         = 0;
char *                  o_length        = 0;

static int              wants_nobanner  = 0;
static int              wants_nolabels  = 0;
static int              lp_or_root      = 0;

static int              _chkopts ( RSTATUS *, CANDIDATE * , FSTATUS * );
static void             free_candidate ( CANDIDATE * );
static int              tsol_check_printer_label_range(char *, const char *);
static int              tsol_lpauth(char *, char *);
static int              secpolicy_chkpolicy(char *policyp);

/**
 ** _validate() - FIND A PRINTER TO HANDLE A REQUEST
 **/

short
_validate(RSTATUS *prs, PSTATUS *pps, PSTATUS *stop_pps, char **prefixp,
         int moving)
{
        register CANDIDATE      *pc             = 0,
                                *pcend,
                                *best_pc        = 0;

        register FSTATUS        *pfs            = 0;

        register CLSTATUS       *pcs            = 0;

        CANDIDATE               *arena          = 0,
                                single;

        size_t                  n;
        int i;

        short                   ret;

        chkprinter_result = 0;
        o_cpi = o_lpi = o_width = o_length = 0;
        wants_nobanner = 0;
        memset (&single, 0, sizeof(single));

        wants_nolabels = 0;
        /*
         * If the system is labeled, the printing of postscript files
         * is restricted.  All users can print postscript files if the
         * file /etc/default/print contains "PRINT_POSTSCRIPT=1".
         * (this is checked by secpolicy_chkpolicy).  Otherwise the
         * user must have PRINT_POSTSCRIPT_AUTH to print postscript files.
         */
        if ((is_system_labeled() &&
            strcmp(prs->request->input_type, "postscript") == 0) &&
            (secpolicy_chkpolicy("PRINT_POSTSCRIPT=") == 0)) {
                if (tsol_lpauth(PRINT_POSTSCRIPT_AUTH, prs->secure->user)
                    == 0) {
                        ret = MDENYDEST;
                        goto Return;
                }
        }
        lp_or_root = 0;

        if (bangequ(prs->secure->user, "root") ||
            bangequ(prs->secure->user, "lp"))
                        lp_or_root = 1;

        if (prefixp)
                *prefixp = prs->request->destination;

        /*
         * If a destination other than "any" was given,
         * see if it exists in our internal tables.
         */
        if (!pps && prs->request->destination &&
            !STREQU(prs->request->destination, NAME_ANY))
                if (((pps = search_pstatus(prs->request->destination)) != NULL) ||
                    ((pcs = search_cstatus(prs->request->destination)) != NULL) &&
                    pcs->class->members)
                        /*EMPTY*/;
                else {
                        ret = MNODEST;
                        goto Return;
                }

        /*
         * If we are trying to avoid a printer, but the request
         * was destined for just that printer, we're out.
         */
        if (pps && pps == stop_pps) {
                ret = MERRDEST;
                goto Return;
        }

        /*
         * If a form was given, see if it exists; if so,
         * see if the user is allowed to use it.
         * If a remote printer was specified, then don't use any local
         * form knowledge.
         */
        if (prs && prs->request && prs->request->form && (pps || pcs)) {
                if ((pfs = search_fstatus(prs->request->form))) {
                        if (lp_or_root || allowed(prs->secure->user,
                                pfs->users_allowed, pfs->users_denied))
                                /*EMPTY*/;
                        else {
                                ret = MDENYMEDIA;
                                goto Return;
                        }
                } else {
                        ret = MNOMEDIA;
                        goto Return;
                }
        }

        /*
         * If the request includes -o options there may be pitch and
         * size and no-banner requests that have to be checked. One
         * could argue that this shouldn't be in the Spooler, because
         * the Spooler's job is SPOOLING, not PRINTING. That's right,
         * except that the Spooler will be making a choice of printers
         * so it has to evaluate carefully: E.g. user wants ANY printer,
         * so we should pick one that can handle what they want.
         *
         * Parse out the important stuff here so we have it when we
         * need it.
         */
        {
                register char           **list,
                                        **pl;

                if (
                        prs->request->options
                     && (list = dashos(prs->request->options))
                ) {
                        for (pl = list ; *pl; pl++)
                                if (STRNEQU(*pl, "cpi=", 4))
                                        o_cpi = Strdup(*pl + 4);
                                else if (STRNEQU(*pl, "lpi=", 4))
                                        o_lpi = Strdup(*pl + 4);
                                else if (STRNEQU(*pl, "width=", 6))
                                        o_width = Strdup(*pl + 6);
                                else if (STRNEQU(*pl, "length=", 7))
                                        o_length = Strdup(*pl + 7);
                                else if (STREQU(*pl, "nobanner"))
                                        wants_nobanner = 1;
                                else if (STREQU(*pl, "nolabels"))
                                        wants_nolabels = 1;
                        freelist (list);
                }
        }

        /*
         * This macro checks that a form has a mandatory print wheel
         * (or character set).
         */
#define CHKMAND(PFS) \
        ( \
                (PFS) \
             && (PFS)->form->chset \
             && !STREQU((PFS)->form->chset, NAME_ANY) \
             && (PFS)->form->mandatory \
        )

        /*
         * This macro checks that the user is allowed to use the
         * printer.
         */
#define CHKU(PRS,PPS) \
        ( \
                lp_or_root \
             || allowed( \
                        (PRS)->secure->user, \
                        (PPS)->users_allowed, \
                        (PPS)->users_denied \
                ) \
        )

        /*
         * This macro checks that the form is allowed on the printer,
         * or is already mounted there.
         * Note: By doing this check we don't have to check that the
         * characteristics of the form, such as pitch, size, or
         * character set, against the printer's capabilities, ASSUMING,
         * of course, that the allow list is correct. That is, the
         * allow list lists forms that have already been checked against
         * the printer!
         */
#define CHKF(PFS,PPS) \
        ( \
                isFormMountedOnPrinter(PPS,PFS) \
             || allowed( \
                        (PFS)->form->name, \
                        (PPS)->forms_allowed, \
                        (PPS)->forms_denied \
                ) \
        )

        /*
         * This macro checks that the print wheel is acceptable
         * for the printer or is mounted. Note: If the printer doesn't
         * take print wheels, the check passes. The check for printers
         * that don't take print wheels is below.
         */
#define CHKPW(PW,PPS) \
        ( \
                !(PPS)->printer->daisy \
             || ( \
                        (PPS)->pwheel_name \
                     && STREQU((PPS)->pwheel_name, (PW)) \
                ) \
             || searchlist((PW), (PPS)->printer->char_sets) \
        )

        /*
         * This macro checks the pitch, page size, and (if need be)
         * the character set. The character set isn't checked if the
         * printer takes print wheels, or if the character set is
         * listed in the printer's alias list.
         * The form has to be checked as well; while we're sure that
         * at least one type for each printer can handle the form's
         * cpi/lpi/etc. characteristics (lpadmin made sure), we aren't
         * sure that ALL the types work.
         */
#define CHKOPTS(PRS,PC,PFS) _chkopts((PRS),(PC),(PFS)) /* was a macro */

        /*
         * This macro checks the acceptance status of a printer.
         * If the request is already assigned to that printer,
         * then it's okay. It's ambiguous what should happen if
         * originally a "-d any" request was accepted, temporarily
         * assigned one printer, then the administrator (1) rejected
         * further requests for the printers and (2) made the
         * temporarily assigned printer unusable for the request.
         * What will happen, of course, is that the request will
         * be canceled, even though the other printers would be okay
         * if not rejecting....but if we were to say, gee it's okay,
         * the request has already been accepted, we may be allowing
         * it on printers that were NEVER accepting. Thus we can
         * continue to accept it only for the printer already assigned.
         */
#define CHKACCEPT(PRS,PPS) \
        ( \
                !((PPS)->status & PS_REJECTED) \
             || (PRS)->printer == (PPS) \
             || moving \
        )

        /*
         * If a print wheel or character set is given, see if it
         * is allowed on the form.
         */
        if (prs->request->charset)
                if (
                        !CHKMAND(pfs)
                     || STREQU(prs->request->charset, pfs->form->chset)
                )
                        /*EMPTY*/;
                else {
                        ret = MDENYMEDIA;
                        chkprinter_result |= PCK_CHARSET;
                        goto Return;
                }

        /*
         * If a single printer was named, check the request against it.
         * Do the accept/reject check late so that we give the most
         * useful information to the user.
         */
        if (pps) {
                (pc = &single)->pps = pps;

                /* Does the printer allow the user? */
                if (!CHKU(prs, pps)) {
                        ret = MDENYDEST;
                        goto Return;
                }

                /* Check printer label range */
                if (is_system_labeled() && prs->secure->slabel != NULL) {
                        if (tsol_check_printer_label_range(
                            prs->secure->slabel,
                            pps->printer->name) == 0) {
                                ret = MDENYDEST;
                                goto Return;
                        }
                }

                /* Does the printer allow the form? */
                if (pfs && !CHKF(pfs, pps)) {
                        ret = MNOMOUNT;
                        goto Return;
                }

                /* Does the printer allow the pwheel? */
                if (
                        prs->request->charset
                     && !CHKPW(prs->request->charset, pps)
                ) {
                        ret = MNOMOUNT;
                        goto Return;
                }

                /* Can printer handle the pitch/size/charset/nobanner? */
                if (!CHKOPTS(prs, pc, pfs)) {
                        ret = MDENYDEST;
                        goto Return;
                }

                /* Is the printer allowing requests? */
                if (!CHKACCEPT(prs, pps)) {
                        ret = MERRDEST;
                        goto Return;
                }

                /* Is there a filter which will convert the input? */
                if (!pickfilter(prs, pc, pfs)) {
                        ret = MNOFILTER;
                        goto Return;
                }

                best_pc = pc;
                ret = MOK;
                goto Return;
        }

        /*
         * Do the acceptance check on the class (if we have one)
         * now so we can proceed with checks on individual printers
         * in the class. Don't toss out the request if it is already
         * assigned a printer just because the class is NOW rejecting.
         */
        if (
                pcs
             && (pcs->status & CS_REJECTED)
             && !moving
             && !prs->printer
        ) {
                ret = MERRDEST;
                goto Return;
        }

        /*
         * Construct a list of printers based on the destination
         * given. Cross off those that aren't accepting requests,
         * that can't take the form, or which the user can't use.
         * See if the list becomes empty.
         */

        if (pcs)
                n = lenlist(pcs->class->members);
        else {
                for (n = 0; PStatus != NULL && PStatus[n] != NULL; n++) ;
        }
        pcend = arena = (CANDIDATE *)Calloc(n, sizeof(CANDIDATE));

        /*
         * Start with a list of printers that are accepting requests.
         * Don't skip a printer if it's rejecting but the request
         * has already been accepted for it.
         */
        if (pcs) {
                register char            **pn;

                for (pn = pcs->class->members; *pn; pn++)
                        if (
                                ((pps = search_pstatus(*pn)) != NULL)
                             && pps != stop_pps
                        )
                                (pcend++)->pps = pps;


        } else
                for (i = 0; PStatus != NULL && PStatus[i] != NULL; i++) {
                        pps = PStatus[i];

                        if (CHKACCEPT(prs, pps) && pps != stop_pps)
                                (pcend++)->pps = pps;
                }

        if (pcend == arena) {
                ret = MERRDEST;
                goto Return;
        }

        /*
         * Clean out printers that the user can't use. We piggy-back
         * the pitch/size/banner checks here because the same error return
         * is given (strange, eh?).
         */
        {
                register CANDIDATE      *pcend2;

                for (pcend2 = pc = arena; pc < pcend; pc++) {
                        if (CHKU(prs, pc->pps) && CHKOPTS(prs, pc, pfs))
                                *pcend2++ = *pc;
                        else
                                free_candidate (pc);
                }

                if (pcend2 == arena) {
                        ret = MDENYDEST;
                        goto Return;
                }
                pcend = pcend2;

        }

        /*
         * Clean out printers that can't mount the form,
         * EXCEPT for printers that already have it mounted:
         */
        if (pfs) {
                register CANDIDATE      *pcend2;

                for (pcend2 = pc = arena; pc < pcend; pc++)
                        if (CHKF(pfs, pc->pps))
                                *pcend2++ = *pc;
                        else
                                free_candidate (pc);

                if (pcend2 == arena) {
                        ret = MNOMOUNT;
                        goto Return;
                }
                pcend = pcend2;

        }

        /*
         * Clean out printers that can't take the print wheel
         * EXCEPT for printers that already have it mounted
         * or printers for which it is a selectable character set:
         */
        if (prs->request->charset) {
                register CANDIDATE      *pcend2;

                for (pcend2 = pc = arena; pc < pcend; pc++)
                        if (CHKPW(prs->request->charset, pc->pps))
                                *pcend2++ = *pc;
                        else
                                free_candidate (pc);

                if (pcend2 == arena) {
                        ret = MNOMOUNT;
                        goto Return;
                }
                pcend = pcend2;

        }

        /*
         * Clean out printers that can't handle the printing
         * and for which there's no filter to convert the input.
         *
         */

        /*
         * Is the form mounted, or is none needed?
         */
#define CHKFMNT(PFS,PPS) (isFormUsableOnPrinter(PPS,PFS))

        /*
         * Is the print-wheel mounted, or is none needed?
         */
#define CHKPWMNT(PRS,PPS) SAME((PPS)->pwheel_name, (PRS)->request->charset)

        /*
         * Do we NOT need a special character set, or can we select
         * it on the printer? Note: Getting this far means that IF
         * the printer has selectable character sets (!daisy) then
         * it can select the one we want.
         */
#define CHKCHSET(PRS,PPS) \
        ( \
                !(PRS)->request->charset \
             || !(PPS)->printer->daisy \
        )

        /*
         * Is the printer able to print now?
         */
#define CHKENB(PPS)      (!((PPS)->status & (PS_DISABLED|PS_FAULTED)))

        /*
         * Is the printer not busy printing another request, or
         * not awaiting an auto-retry after a fault?
         */
#define CHKFREE(PPS)     (!((PPS)->status & (PS_BUSY|PS_LATER)))

        {
                register CANDIDATE      *pcend2;

                for (pcend2 = pc = arena; pc < pcend; pc++)
                        if (pickfilter(prs, pc, pfs)) {

                                /*
                                 * Compute a ``weight'' for this printer,
                                 * based on its status. We'll later pick
                                 * the printer with the highest weight.
                                 */
                                pc->weight = 0;
                                if (!pc->fast && !pc->slow)
                                        pc->weight += WEIGHT_NOFILTER;
                                if (CHKFREE(pc->pps))
                                        pc->weight += WEIGHT_FREE;
                                if (CHKENB(pc->pps))
                                        pc->weight += WEIGHT_ENABLED;
                                if (CHKFMNT(pfs, pc->pps))
                                        pc->weight += WEIGHT_MOUNTED;
                                if (CHKPWMNT(prs, pc->pps))
                                        pc->weight += WEIGHT_MOUNTED;
                                if (CHKCHSET(prs, pc->pps))
                                        pc->weight += WEIGHT_SELECTS;

#if     defined(FILTER_EARLY_OUT)
                                if (pc->weight == WEIGHT_MAX) {
                                        /*
                                         * This is the one!
                                         */
                                        best_pc = pc;
                                        ret = MOK;
                                        goto Return;
                                }
#endif
                                /*
                                 * This is a candidate!
                                 */
                                *pcend2++ = *pc;

                        } else
                                /*
                                 * No filter for this one!
                                 */
                                free_candidate (pc);

                if (pcend2 == arena) {
                        ret = MNOFILTER;
                        goto Return;
                }
                pcend = pcend2;

        }

        if (pcend - arena == 1) {
                best_pc = arena;
                ret = MOK;
                goto Return;
        }
        /*
         * Clean out local printers
         * where the request is outside the printer label range.
         */
        {
                register CANDIDATE      *pcend2 = pcend;

                if (is_system_labeled()) {
                        for (pcend2 = pc = arena; pc < pcend; pc++) {
                                if (tsol_check_printer_label_range(
                                    prs->secure->slabel,
                                    pps->printer->name) == 1)
                                        *pcend2++ = *pc;
                                else
                                        free_candidate(pc);
                        }
                }

                if (pcend2 == arena) {
                        ret = MDENYDEST;
                        goto Return;
                }
                pcend = pcend2;
        }

#if     defined(OTHER_FACTORS)
        /*
         * Here you might want to add code that considers
         * other factors: the size of the file(s) to be
         * printed ("prs->secure->size") in relation to the
         * printer (e.g. printer A gets mostly large
         * files, printer B gets mostly small files); the
         * number/total-size of requests currently queued
         * for the printer; etc.
         *
         * If your code includes eliminating printers drop them
         * from the list (as done in several places above).
         * Otherwise, your code should add weights to the weight
         * already computed. Change the WEIGHT_MAX, increase the
         * other WEIGHT_X values to compensate, etc., as appropriate.
         */
        ;
#endif

        /*
         * Pick the best printer from a list of eligible candidates.
         */
        best_pc = arena;
        for (pc = arena + 1; pc < pcend; pc++)
                if (pc->weight > best_pc->weight)
                        best_pc = pc;
        ret = MOK;

        /*
         * Branch to here if MOK and/or if things have been allocated.
         */
Return: if (ret == MOK) {
                register USER           *pu = Getuser(prs->secure->user);

                register char           *pwheel_name;

                PSTATUS                 *oldpps = prs->printer;


                /*
                 * We are going to accept this print request, having
                 * found a printer for it. This printer will be assigned
                 * to the request, although this assignment may be
                 * temporary if other printers qualify and this printer
                 * is changed to no longer qualify. Qualification in
                 * this context includes being ready to print!
                 */
                prs->printer = best_pc->pps;
                load_str (&(prs->printer_type), best_pc->printer_type);

                /*
                 * Assign the form (if any) to the request. Adjust
                 * the number of requests queued for old and new form
                 * accordingly.
                 */
                if (prs->form != pfs) {
                        unqueue_form (prs);
                        queue_form (prs, pfs);
                }

                /*
                 * Ditto for the print wheel, except include here the
                 * print wheel needed by the form.
                 * CAUTION: When checking this request later, don't
                 * refuse to service it if the print wheel for the
                 * form isn't mounted but the form is; a mounted form
                 * overrides its other needs. Don't be confused by the
                 * name of the bit, RSS_PWMAND; a printer that prints
                 * this request MUST have the print wheel mounted
                 * (if it takes print wheels) if the user asked for
                 * a particular print wheel.
                 */
                prs->status &= ~RSS_PWMAND;
                if (CHKMAND(pfs))
                        pwheel_name = pfs->form->chset;
                else
                        if ((pwheel_name = prs->request->charset) != NULL)
                                prs->status |= RSS_PWMAND;

                if (!SAME(pwheel_name, prs->pwheel_name)) {
                        unqueue_pwheel (prs);
                        queue_pwheel (prs, pwheel_name);
                }

                /*
                 * Adjust the priority to lie within the limits allowed
                 * for the user (this is a silent adjustment as required).
                 * CURRENTLY, ONLY NEW REQUESTS WILL GET QUEUED ACCORDING
                 * TO THIS PRIORITY. EXISTING REQUESTS BEING (RE)EVALUATED
                 * WILL NOT BE REQUEUED.
                 * A wild priority is changed to the default, or the
                 * limit, whichever is the lower priority (higher value).
                 */
                if (prs->request->priority < 0 || 39 < prs->request->priority)
                        prs->request->priority = getdfltpri();
                if (pu && prs->request->priority < pu->priority_limit)
                        prs->request->priority = pu->priority_limit;

                /*
                 * If a filter is involved, change the number of
                 * copies to 1 (if the filter handles it).
                 */
                if (
                        (best_pc->fast || best_pc->slow)
                     && (best_pc->flags & FPARM_COPIES)
                     && prs->request->copies > 1
                )
                        prs->copies = 1;
                else
                        /*
                         * We use two ".copies" because we don't
                         * want to lose track of the number requested,
                         * but do want to mark the number the interface
                         * program is to handle. Here is the best
                         * place to know this.
                         */
                        prs->copies = prs->request->copies;

                if (best_pc->slow) {
                        /*
                         * If the filter has changed, the request will
                         * have to be refiltered. This may mean stopping
                         * a currently running filter or interface.
                         */
                        if (!SAME(best_pc->slow, prs->slow)) {

                            if (prs->request->outcome & RS_FILTERED)
                                prs->request->outcome &= ~RS_FILTERED;

                            if (
                                prs->request->outcome & RS_FILTERING
                             && !(prs->request->outcome & RS_STOPPED)
                            ) {
                                prs->request->outcome |= RS_REFILTER;
                                prs->request->outcome |= RS_STOPPED;
                                terminate (prs->exec);

                            } else if (
                                prs->request->outcome & RS_PRINTING
                             && !(prs->request->outcome & RS_STOPPED)
                            ) {
                                prs->request->outcome |= RS_STOPPED;
                                terminate (oldpps->exec);
                            }

                        }

                        load_str (&(prs->slow), best_pc->slow);
                        /* Assumption: if there is a slow filter,
                         * there is an output_type
                         */

                        load_str (&(prs->output_type), best_pc->output_type);
                } else
                        unload_str (&(prs->slow));

                load_str (&(prs->fast), best_pc->fast);

                if (prs->request->actions & ACT_FAST && prs->slow) {
                        if (prs->fast) {
                                prs->fast = makestr(
                                        prs->slow,
                                        "|",
                                        prs->fast,
                                        (char *)0
                                );
                                Free (prs->slow);
                        } else
                                prs->fast = prs->slow;
                        prs->slow = 0;
                }

        }


        /*
         * Free the space allocated for the candidates, INCLUDING
         * the one chosen. Any allocated space in the chosen candidate
         * that has to be saved should have been COPIED already.
         */
        if (arena) {
                for (pc = arena; pc < pcend; pc++)
                        free_candidate (pc);
                Free ((char *)arena);
        } else if (best_pc)
                free_candidate (best_pc);

        if (o_length)
                Free (o_length);
        if (o_width)
                Free (o_width);
        if (o_lpi)
                Free (o_lpi);
        if (o_cpi)
                Free (o_cpi);


        /*
         * The following value is valid ONLY IF the request
         * is canceled or rejected. Not all requests that
         * we fail in this routine are tossed out!
         */
        prs->reason = ret;


        return (ret);
}

/**
 ** _chkopts() - CHECK -o OPTIONS
 **/

static int
_chkopts(RSTATUS *prs, CANDIDATE *pc, FSTATUS *pfs)
{
        unsigned long           ret     = 0;
        unsigned long           chk     = 0;

        char *                  charset;
        char *                  cpi     = 0;
        char *                  lpi     = 0;
        char *                  width   = 0;
        char *                  length  = 0;
        char *                  paper = NULL;

        char **                 pt;
        int                     nobanner_not_allowed = 0;


        /*
         * If we have a form, it overrides whatever print characteristics
         * the user gave.
         */
        if (pfs) {
                cpi = pfs->cpi;
                lpi = pfs->lpi;
                width = pfs->pwid;
                length = pfs->plen;
                paper = pfs->form->paper;
        } else {
                cpi = o_cpi;
                lpi = o_lpi;
                width = o_width;
                length = o_length;
        }

        /*
         * If the printer takes print wheels, or the character set
         * the user wants is listed in the character set map for this
         * printer, we needn't check if the printer can handle the
         * character set. (Note: The check for the print wheel case
         * is done elsewhere.)
         */

        if (pc->pps->printer->daisy ||
            search_cslist(prs->request->charset, pc->pps->printer->char_sets))
                charset = 0;
        else
                charset = prs->request->charset;

        pc->printer_types = 0;
        for (pt = pc->pps->printer->printer_types; *pt; pt++) {
                unsigned long           this;

                if (paper) {
                        if (allowed(paper,pc->pps->paper_allowed,NULL)) {
                                addlist (&(pc->printer_types), *pt);
                        } else {
                                ret |= PCK_PAPER;
                        }
                } else {
                        this = chkprinter(*pt, cpi, lpi, length, width,
                                charset);
                        if (this == 0)
                                addlist(&(pc->printer_types), *pt);
                        chk |= this;
                }
        }
        if (!pc->printer_types)
                ret |= chk;

        /*
         * If the system is labeled, then user who wants 'nolabels' must
         * have PRINT_UNLABELED_AUTH authorizations to allow it.
         */
        if (is_system_labeled() && (wants_nolabels == 1)) {
                if (!tsol_lpauth(PRINT_UNLABELED_AUTH, prs->secure->user)) {
                        /* if not authorized, remove "nolabels" from options */
                        register char           **list;
                        if (prs->request->options &&
                            (list = dashos(prs->request->options))) {
                                dellist(&list, "nolabels");
                                free(prs->request->options);
                                prs->request->options = sprintlist(list);
                        }
                }
        }


        if (pc->pps->printer->banner == BAN_ALWAYS) {
                /* delete "nobanner" */
                char **list;

                /*
                 * If the system is labeled, users must have
                 * PRINT_NOBANNER_AUTH authorization to print
                 * without a banner.
                 */
                if (is_system_labeled()) {
                        if (wants_nobanner == 1) {
                                if (tsol_lpauth(PRINT_NOBANNER_AUTH,
                                        prs->secure->user) == 0) {
                                        nobanner_not_allowed = 1;
                                }
                        }

                }
                else if ((wants_nobanner == 1) && (lp_or_root != 1)) {
                        nobanner_not_allowed = 1;
                }
                if (nobanner_not_allowed == 1) {
                        /* Take out 'nobanner' from request options. */
                        if (prs->request->options &&
                            (list = dashos(prs->request->options))) {
                                dellist(&list, "nobanner");
                                free(prs->request->options);
                                prs->request->options = sprintlist(list);
                        }
                }
        } else if (pc->pps->printer->banner == BAN_NEVER) {
                if (wants_nobanner == 0) {
                        /* add "nobanner" */
                        char **list = NULL;

                        if (prs->request->options) {
                                list = dashos(prs->request->options);
                                free(prs->request->options);
                        }
                        appendlist(&list, "nobanner");
                        prs->request->options = sprintlist(list);
                }
        } else /* if (pc->pps->printer->banner == BAN_OPTIONAL) */ {
                /* it is optional, leave it alone */
        }

        chkprinter_result |= ret;
        return (ret == 0);
}

/**
 ** free_candidate()
 **/

static void
free_candidate(CANDIDATE *pc)
{
        if (pc->slow)
                unload_str (&(pc->slow));
        if (pc->fast)
                unload_str (&(pc->fast));
        if (pc->printer_types) {
                freelist (pc->printer_types);
                pc->printer_types = 0;
        }
        if (pc->printer_type)
                unload_str (&(pc->printer_type));
        if (pc->output_type)
                unload_str (&(pc->output_type));
        return;
}

static int
tsol_check_printer_label_range(char *slabel, const char *printer)
{
        int                     in_range = 0;
        int                     err = 0;
        m_range_t               *range;
        m_label_t       *sl = NULL;

        if (slabel == NULL)
                return (0);

        if ((err =
            (str_to_label(slabel, &sl, USER_CLEAR, L_NO_CORRECTION, &in_range)))
            == -1) {
                /* stobsl error on printer max label */
                return (0);
        }
        if ((range = getdevicerange(printer)) == NULL) {
                m_label_free(sl);
                return (0);
        }

        /* blinrange returns true (1) if in range, false (0) if not */
        in_range = blinrange(sl, range);

        m_label_free(sl);
        m_label_free(range->lower_bound);
        m_label_free(range->upper_bound);
        free(range);

        return (in_range);
}

/*
 * Given a character string with a "username" or "system!username"
 * this function returns a pointer to "username"
 */
static int
tsol_lpauth(char *auth, char *in_name)
{
        char *cp;
        int res;

        if ((cp = strchr(in_name, '@')) != NULL) {
                /* user@system */
                *cp = '\0';
                res = chkauthattr(auth, in_name);
                *cp = '@';
        } else if ((cp = strchr(in_name, '!')) != NULL)
                /* system!user */
                res = chkauthattr(auth, cp+1);
        else
                /* user */
                res = chkauthattr(auth, in_name);

        return (res);
}

#define POLICY_FILE     "/etc/default/print"

int
secpolicy_chkpolicy(char *policyp)
{
        char *option;
        int opt_val;

        if (policyp == NULL)
                return (0);
        opt_val = 0;
        if (defopen(POLICY_FILE) == 0) {

                defcntl(DC_SETFLAGS, DC_STD & ~DC_CASE); /* ignore case */

                if ((option = defread(policyp)) != NULL)
                        opt_val = atoi(option);
        }
        (void) defopen((char *)NULL);
        syslog(LOG_DEBUG, "--- Policy %s, opt_val==%d",
            policyp ? policyp : "NULL", opt_val);
        return (opt_val);
}