Home | History | Annotate | Download | only in cups
      1 /*
      2  * User, system, and password routines for CUPS.
      3  *
      4  * Copyright 2007-2017 by Apple Inc.
      5  * Copyright 1997-2006 by Easy Software Products.
      6  *
      7  * These coded instructions, statements, and computer programs are the
      8  * property of Apple Inc. and are protected by Federal copyright
      9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
     10  * which should have been included with this file.  If this file is
     11  * missing or damaged, see the license at "http://www.cups.org/".
     12  *
     13  * This file is subject to the Apple OS-Developed Software exception.
     14  */
     15 
     16 /*
     17  * Include necessary headers...
     18  */
     19 
     20 #include "cups-private.h"
     21 #include <stdlib.h>
     22 #include <sys/stat.h>
     23 #ifdef WIN32
     24 #  include <windows.h>
     25 #else
     26 #  include <pwd.h>
     27 #  include <termios.h>
     28 #  include <sys/utsname.h>
     29 #endif /* WIN32 */
     30 
     31 
     32 /*
     33  * Local constants...
     34  */
     35 
     36 #ifdef __APPLE__
     37 #  define kCUPSPrintingPrefs	CFSTR("org.cups.PrintingPrefs")
     38 #  define kAllowAnyRootKey	CFSTR("AllowAnyRoot")
     39 #  define kAllowExpiredCertsKey	CFSTR("AllowExpiredCerts")
     40 #  define kEncryptionKey	CFSTR("Encryption")
     41 #  define kGSSServiceNameKey	CFSTR("GSSServiceName")
     42 #  define kSSLOptionsKey	CFSTR("SSLOptions")
     43 #  define kTrustOnFirstUseKey	CFSTR("TrustOnFirstUse")
     44 #  define kValidateCertsKey	CFSTR("ValidateCerts")
     45 #endif /* __APPLE__ */
     46 
     47 #define _CUPS_PASSCHAR	'*'		/* Character that is echoed for password */
     48 
     49 
     50 /*
     51  * Local types...
     52  */
     53 
     54 typedef struct _cups_client_conf_s	/**** client.conf config data ****/
     55 {
     56 #ifdef HAVE_SSL
     57   int			ssl_options;	/* SSLOptions values */
     58 #endif /* HAVE_SSL */
     59   int			trust_first,	/* Trust on first use? */
     60 			any_root,	/* Allow any (e.g., self-signed) root */
     61 			expired_certs,	/* Allow expired certs */
     62 			validate_certs;	/* Validate certificates */
     63   http_encryption_t	encryption;	/* Encryption setting */
     64   char			user[65],	/* User name */
     65 			server_name[256];
     66 					/* Server hostname */
     67 #ifdef HAVE_GSSAPI
     68   char			gss_service_name[32];
     69   					/* Kerberos service name */
     70 #endif /* HAVE_GSSAPI */
     71 } _cups_client_conf_t;
     72 
     73 
     74 /*
     75  * Local functions...
     76  */
     77 
     78 #ifdef __APPLE__
     79 static int	cups_apple_get_boolean(CFStringRef key, int *value);
     80 static int	cups_apple_get_string(CFStringRef key, char *value, size_t valsize);
     81 #endif /* __APPLE__ */
     82 static int	cups_boolean_value(const char *value);
     83 static void	cups_finalize_client_conf(_cups_client_conf_t *cc);
     84 static void	cups_init_client_conf(_cups_client_conf_t *cc);
     85 static void	cups_read_client_conf(cups_file_t *fp, _cups_client_conf_t *cc);
     86 static void	cups_set_default_ipp_port(_cups_globals_t *cg);
     87 static void	cups_set_encryption(_cups_client_conf_t *cc, const char *value);
     88 #ifdef HAVE_GSSAPI
     89 static void	cups_set_gss_service_name(_cups_client_conf_t *cc, const char *value);
     90 #endif /* HAVE_GSSAPI */
     91 static void	cups_set_server_name(_cups_client_conf_t *cc, const char *value);
     92 #ifdef HAVE_SSL
     93 static void	cups_set_ssl_options(_cups_client_conf_t *cc, const char *value);
     94 #endif /* HAVE_SSL */
     95 static void	cups_set_user(_cups_client_conf_t *cc, const char *value);
     96 
     97 
     98 /*
     99  * 'cupsEncryption()' - Get the current encryption settings.
    100  *
    101  * The default encryption setting comes from the CUPS_ENCRYPTION
    102  * environment variable, then the ~/.cups/client.conf file, and finally the
    103  * /etc/cups/client.conf file. If not set, the default is
    104  * @code HTTP_ENCRYPTION_IF_REQUESTED@.
    105  *
    106  * Note: The current encryption setting is tracked separately for each thread
    107  * in a program. Multi-threaded programs that override the setting via the
    108  * @link cupsSetEncryption@ function need to do so in each thread for the same
    109  * setting to be used.
    110  */
    111 
    112 http_encryption_t			/* O - Encryption settings */
    113 cupsEncryption(void)
    114 {
    115   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    116 
    117 
    118   if (cg->encryption == (http_encryption_t)-1)
    119     _cupsSetDefaults();
    120 
    121   return (cg->encryption);
    122 }
    123 
    124 
    125 /*
    126  * 'cupsGetPassword()' - Get a password from the user.
    127  *
    128  * Uses the current password callback function. Returns @code NULL@ if the
    129  * user does not provide a password.
    130  *
    131  * Note: The current password callback function is tracked separately for each
    132  * thread in a program. Multi-threaded programs that override the setting via
    133  * the @link cupsSetPasswordCB@ or @link cupsSetPasswordCB2@ functions need to
    134  * do so in each thread for the same function to be used.
    135  *
    136  * @exclude all@
    137  */
    138 
    139 const char *				/* O - Password */
    140 cupsGetPassword(const char *prompt)	/* I - Prompt string */
    141 {
    142   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    143 
    144 
    145   return ((cg->password_cb)(prompt, NULL, NULL, NULL, cg->password_data));
    146 }
    147 
    148 
    149 /*
    150  * 'cupsGetPassword2()' - Get a password from the user using the current
    151  *                        password callback.
    152  *
    153  * Uses the current password callback function. Returns @code NULL@ if the
    154  * user does not provide a password.
    155  *
    156  * Note: The current password callback function is tracked separately for each
    157  * thread in a program. Multi-threaded programs that override the setting via
    158  * the @link cupsSetPasswordCB2@ function need to do so in each thread for the
    159  * same function to be used.
    160  *
    161  * @since CUPS 1.4/macOS 10.6@
    162  */
    163 
    164 const char *				/* O - Password */
    165 cupsGetPassword2(const char *prompt,	/* I - Prompt string */
    166 		 http_t     *http,	/* I - Connection to server or @code CUPS_HTTP_DEFAULT@ */
    167 		 const char *method,	/* I - Request method ("GET", "POST", "PUT") */
    168 		 const char *resource)	/* I - Resource path */
    169 {
    170   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    171 
    172 
    173   if (!http)
    174     http = _cupsConnect();
    175 
    176   return ((cg->password_cb)(prompt, http, method, resource, cg->password_data));
    177 }
    178 
    179 
    180 /*
    181  * 'cupsServer()' - Return the hostname/address of the current server.
    182  *
    183  * The default server comes from the CUPS_SERVER environment variable, then the
    184  * ~/.cups/client.conf file, and finally the /etc/cups/client.conf file. If not
    185  * set, the default is the local system - either "localhost" or a domain socket
    186  * path.
    187  *
    188  * The returned value can be a fully-qualified hostname, a numeric IPv4 or IPv6
    189  * address, or a domain socket pathname.
    190  *
    191  * Note: The current server is tracked separately for each thread in a program.
    192  * Multi-threaded programs that override the server via the
    193  * @link cupsSetServer@ function need to do so in each thread for the same
    194  * server to be used.
    195  */
    196 
    197 const char *				/* O - Server name */
    198 cupsServer(void)
    199 {
    200   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    201 
    202 
    203   if (!cg->server[0])
    204     _cupsSetDefaults();
    205 
    206   return (cg->server);
    207 }
    208 
    209 
    210 /*
    211  * 'cupsSetClientCertCB()' - Set the client certificate callback.
    212  *
    213  * Pass @code NULL@ to restore the default callback.
    214  *
    215  * Note: The current certificate callback is tracked separately for each thread
    216  * in a program. Multi-threaded programs that override the callback need to do
    217  * so in each thread for the same callback to be used.
    218  *
    219  * @since CUPS 1.5/macOS 10.7@
    220  */
    221 
    222 void
    223 cupsSetClientCertCB(
    224     cups_client_cert_cb_t cb,		/* I - Callback function */
    225     void                  *user_data)	/* I - User data pointer */
    226 {
    227   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    228 
    229 
    230   cg->client_cert_cb	= cb;
    231   cg->client_cert_data	= user_data;
    232 }
    233 
    234 
    235 /*
    236  * 'cupsSetCredentials()' - Set the default credentials to be used for SSL/TLS
    237  *			    connections.
    238  *
    239  * Note: The default credentials are tracked separately for each thread in a
    240  * program. Multi-threaded programs that override the setting need to do so in
    241  * each thread for the same setting to be used.
    242  *
    243  * @since CUPS 1.5/macOS 10.7@
    244  */
    245 
    246 int					/* O - Status of call (0 = success) */
    247 cupsSetCredentials(
    248     cups_array_t *credentials)		/* I - Array of credentials */
    249 {
    250   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    251 
    252 
    253   if (cupsArrayCount(credentials) < 1)
    254     return (-1);
    255 
    256 #ifdef HAVE_SSL
    257   _httpFreeCredentials(cg->tls_credentials);
    258   cg->tls_credentials = _httpCreateCredentials(credentials);
    259 #endif /* HAVE_SSL */
    260 
    261   return (cg->tls_credentials ? 0 : -1);
    262 }
    263 
    264 
    265 /*
    266  * 'cupsSetEncryption()' - Set the encryption preference.
    267  *
    268  * The default encryption setting comes from the CUPS_ENCRYPTION
    269  * environment variable, then the ~/.cups/client.conf file, and finally the
    270  * /etc/cups/client.conf file. If not set, the default is
    271  * @code HTTP_ENCRYPTION_IF_REQUESTED@.
    272  *
    273  * Note: The current encryption setting is tracked separately for each thread
    274  * in a program. Multi-threaded programs that override the setting need to do
    275  * so in each thread for the same setting to be used.
    276  */
    277 
    278 void
    279 cupsSetEncryption(http_encryption_t e)	/* I - New encryption preference */
    280 {
    281   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    282 
    283 
    284   cg->encryption = e;
    285 
    286   if (cg->http)
    287     httpEncryption(cg->http, e);
    288 }
    289 
    290 
    291 /*
    292  * 'cupsSetPasswordCB()' - Set the password callback for CUPS.
    293  *
    294  * Pass @code NULL@ to restore the default (console) password callback, which
    295  * reads the password from the console. Programs should call either this
    296  * function or @link cupsSetPasswordCB2@, as only one callback can be registered
    297  * by a program per thread.
    298  *
    299  * Note: The current password callback is tracked separately for each thread
    300  * in a program. Multi-threaded programs that override the callback need to do
    301  * so in each thread for the same callback to be used.
    302  *
    303  * @exclude all@
    304  */
    305 
    306 void
    307 cupsSetPasswordCB(cups_password_cb_t cb)/* I - Callback function */
    308 {
    309   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    310 
    311 
    312   if (cb == (cups_password_cb_t)0)
    313     cg->password_cb = (cups_password_cb2_t)_cupsGetPassword;
    314   else
    315     cg->password_cb = (cups_password_cb2_t)cb;
    316 
    317   cg->password_data = NULL;
    318 }
    319 
    320 
    321 /*
    322  * 'cupsSetPasswordCB2()' - Set the advanced password callback for CUPS.
    323  *
    324  * Pass @code NULL@ to restore the default (console) password callback, which
    325  * reads the password from the console. Programs should call either this
    326  * function or @link cupsSetPasswordCB2@, as only one callback can be registered
    327  * by a program per thread.
    328  *
    329  * Note: The current password callback is tracked separately for each thread
    330  * in a program. Multi-threaded programs that override the callback need to do
    331  * so in each thread for the same callback to be used.
    332  *
    333  * @since CUPS 1.4/macOS 10.6@
    334  */
    335 
    336 void
    337 cupsSetPasswordCB2(
    338     cups_password_cb2_t cb,		/* I - Callback function */
    339     void                *user_data)	/* I - User data pointer */
    340 {
    341   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    342 
    343 
    344   if (cb == (cups_password_cb2_t)0)
    345     cg->password_cb = (cups_password_cb2_t)_cupsGetPassword;
    346   else
    347     cg->password_cb = cb;
    348 
    349   cg->password_data = user_data;
    350 }
    351 
    352 
    353 /*
    354  * 'cupsSetServer()' - Set the default server name and port.
    355  *
    356  * The "server" string can be a fully-qualified hostname, a numeric
    357  * IPv4 or IPv6 address, or a domain socket pathname. Hostnames and numeric IP
    358  * addresses can be optionally followed by a colon and port number to override
    359  * the default port 631, e.g. "hostname:8631". Pass @code NULL@ to restore the
    360  * default server name and port.
    361  *
    362  * Note: The current server is tracked separately for each thread in a program.
    363  * Multi-threaded programs that override the server need to do so in each
    364  * thread for the same server to be used.
    365  */
    366 
    367 void
    368 cupsSetServer(const char *server)	/* I - Server name */
    369 {
    370   char		*options,		/* Options */
    371 		*port;			/* Pointer to port */
    372   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    373 
    374 
    375   if (server)
    376   {
    377     strlcpy(cg->server, server, sizeof(cg->server));
    378 
    379     if (cg->server[0] != '/' && (options = strrchr(cg->server, '/')) != NULL)
    380     {
    381       *options++ = '\0';
    382 
    383       if (!strcmp(options, "version=1.0"))
    384         cg->server_version = 10;
    385       else if (!strcmp(options, "version=1.1"))
    386         cg->server_version = 11;
    387       else if (!strcmp(options, "version=2.0"))
    388         cg->server_version = 20;
    389       else if (!strcmp(options, "version=2.1"))
    390         cg->server_version = 21;
    391       else if (!strcmp(options, "version=2.2"))
    392         cg->server_version = 22;
    393     }
    394     else
    395       cg->server_version = 20;
    396 
    397     if (cg->server[0] != '/' && (port = strrchr(cg->server, ':')) != NULL &&
    398         !strchr(port, ']') && isdigit(port[1] & 255))
    399     {
    400       *port++ = '\0';
    401 
    402       cg->ipp_port = atoi(port);
    403     }
    404 
    405     if (!cg->ipp_port)
    406       cups_set_default_ipp_port(cg);
    407 
    408     if (cg->server[0] == '/')
    409       strlcpy(cg->servername, "localhost", sizeof(cg->servername));
    410     else
    411       strlcpy(cg->servername, cg->server, sizeof(cg->servername));
    412   }
    413   else
    414   {
    415     cg->server[0]      = '\0';
    416     cg->servername[0]  = '\0';
    417     cg->server_version = 20;
    418     cg->ipp_port       = 0;
    419   }
    420 
    421   if (cg->http)
    422   {
    423     httpClose(cg->http);
    424     cg->http = NULL;
    425   }
    426 }
    427 
    428 
    429 /*
    430  * 'cupsSetServerCertCB()' - Set the server certificate callback.
    431  *
    432  * Pass @code NULL@ to restore the default callback.
    433  *
    434  * Note: The current credentials callback is tracked separately for each thread
    435  * in a program. Multi-threaded programs that override the callback need to do
    436  * so in each thread for the same callback to be used.
    437  *
    438  * @since CUPS 1.5/macOS 10.7@
    439  */
    440 
    441 void
    442 cupsSetServerCertCB(
    443     cups_server_cert_cb_t cb,		/* I - Callback function */
    444     void		  *user_data)	/* I - User data pointer */
    445 {
    446   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    447 
    448 
    449   cg->server_cert_cb	= cb;
    450   cg->server_cert_data	= user_data;
    451 }
    452 
    453 
    454 /*
    455  * 'cupsSetUser()' - Set the default user name.
    456  *
    457  * Pass @code NULL@ to restore the default user name.
    458  *
    459  * Note: The current user name is tracked separately for each thread in a
    460  * program. Multi-threaded programs that override the user name need to do so
    461  * in each thread for the same user name to be used.
    462  */
    463 
    464 void
    465 cupsSetUser(const char *user)		/* I - User name */
    466 {
    467   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    468 
    469 
    470   if (user)
    471     strlcpy(cg->user, user, sizeof(cg->user));
    472   else
    473     cg->user[0] = '\0';
    474 }
    475 
    476 
    477 /*
    478  * 'cupsSetUserAgent()' - Set the default HTTP User-Agent string.
    479  *
    480  * Setting the string to NULL forces the default value containing the CUPS
    481  * version, IPP version, and operating system version and architecture.
    482  *
    483  * @since CUPS 1.7/macOS 10.9@
    484  */
    485 
    486 void
    487 cupsSetUserAgent(const char *user_agent)/* I - User-Agent string or @code NULL@ */
    488 {
    489   _cups_globals_t	*cg = _cupsGlobals();
    490 					/* Thread globals */
    491 #ifdef WIN32
    492   SYSTEM_INFO		sysinfo;	/* System information */
    493   OSVERSIONINFO		version;	/* OS version info */
    494 #else
    495   struct utsname	name;		/* uname info */
    496 #endif /* WIN32 */
    497 
    498 
    499   if (user_agent)
    500   {
    501     strlcpy(cg->user_agent, user_agent, sizeof(cg->user_agent));
    502     return;
    503   }
    504 
    505 #ifdef WIN32
    506   version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    507   GetVersionEx(&version);
    508   GetNativeSystemInfo(&sysinfo);
    509 
    510   snprintf(cg->user_agent, sizeof(cg->user_agent),
    511            CUPS_MINIMAL " (Windows %d.%d; %s) IPP/2.0",
    512 	   version.dwMajorVersion, version.dwMinorVersion,
    513 	   sysinfo.wProcessorArchitecture
    514 	       == PROCESSOR_ARCHITECTURE_AMD64 ? "amd64" :
    515 	       sysinfo.wProcessorArchitecture
    516 		   == PROCESSOR_ARCHITECTURE_ARM ? "arm" :
    517 	       sysinfo.wProcessorArchitecture
    518 		   == PROCESSOR_ARCHITECTURE_IA64 ? "ia64" :
    519 	       sysinfo.wProcessorArchitecture
    520 		   == PROCESSOR_ARCHITECTURE_INTEL ? "intel" :
    521 	       "unknown");
    522 
    523 #else
    524   uname(&name);
    525 
    526   snprintf(cg->user_agent, sizeof(cg->user_agent),
    527            CUPS_MINIMAL " (%s %s; %s) IPP/2.0",
    528 	   name.sysname, name.release, name.machine);
    529 #endif /* WIN32 */
    530 }
    531 
    532 
    533 /*
    534  * 'cupsUser()' - Return the current user's name.
    535  *
    536  * Note: The current user name is tracked separately for each thread in a
    537  * program. Multi-threaded programs that override the user name with the
    538  * @link cupsSetUser@ function need to do so in each thread for the same user
    539  * name to be used.
    540  */
    541 
    542 const char *				/* O - User name */
    543 cupsUser(void)
    544 {
    545   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    546 
    547 
    548   if (!cg->user[0])
    549     _cupsSetDefaults();
    550 
    551   return (cg->user);
    552 }
    553 
    554 
    555 /*
    556  * 'cupsUserAgent()' - Return the default HTTP User-Agent string.
    557  *
    558  * @since CUPS 1.7/macOS 10.9@
    559  */
    560 
    561 const char *				/* O - User-Agent string */
    562 cupsUserAgent(void)
    563 {
    564   _cups_globals_t *cg = _cupsGlobals();	/* Thread globals */
    565 
    566 
    567   if (!cg->user_agent[0])
    568     cupsSetUserAgent(NULL);
    569 
    570   return (cg->user_agent);
    571 }
    572 
    573 
    574 /*
    575  * '_cupsGetPassword()' - Get a password from the user.
    576  */
    577 
    578 const char *				/* O - Password or @code NULL@ if none */
    579 _cupsGetPassword(const char *prompt)	/* I - Prompt string */
    580 {
    581 #ifdef WIN32
    582   HANDLE		tty;		/* Console handle */
    583   DWORD			mode;		/* Console mode */
    584   char			passch,		/* Current key press */
    585 			*passptr,	/* Pointer into password string */
    586 			*passend;	/* End of password string */
    587   DWORD			passbytes;	/* Bytes read */
    588   _cups_globals_t	*cg = _cupsGlobals();
    589 					/* Thread globals */
    590 
    591 
    592  /*
    593   * Disable input echo and set raw input...
    594   */
    595 
    596   if ((tty = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE)
    597     return (NULL);
    598 
    599   if (!GetConsoleMode(tty, &mode))
    600     return (NULL);
    601 
    602   if (!SetConsoleMode(tty, 0))
    603     return (NULL);
    604 
    605  /*
    606   * Display the prompt...
    607   */
    608 
    609   printf("%s ", prompt);
    610   fflush(stdout);
    611 
    612  /*
    613   * Read the password string from /dev/tty until we get interrupted or get a
    614   * carriage return or newline...
    615   */
    616 
    617   passptr = cg->password;
    618   passend = cg->password + sizeof(cg->password) - 1;
    619 
    620   while (ReadFile(tty, &passch, 1, &passbytes, NULL))
    621   {
    622     if (passch == 0x0A || passch == 0x0D)
    623     {
    624      /*
    625       * Enter/return...
    626       */
    627 
    628       break;
    629     }
    630     else if (passch == 0x08 || passch == 0x7F)
    631     {
    632      /*
    633       * Backspace/delete (erase character)...
    634       */
    635 
    636       if (passptr > cg->password)
    637       {
    638         passptr --;
    639         fputs("\010 \010", stdout);
    640       }
    641       else
    642         putchar(0x07);
    643     }
    644     else if (passch == 0x15)
    645     {
    646      /*
    647       * CTRL+U (erase line)
    648       */
    649 
    650       if (passptr > cg->password)
    651       {
    652 	while (passptr > cg->password)
    653 	{
    654           passptr --;
    655           fputs("\010 \010", stdout);
    656         }
    657       }
    658       else
    659         putchar(0x07);
    660     }
    661     else if (passch == 0x03)
    662     {
    663      /*
    664       * CTRL+C...
    665       */
    666 
    667       passptr = cg->password;
    668       break;
    669     }
    670     else if ((passch & 255) < 0x20 || passptr >= passend)
    671       putchar(0x07);
    672     else
    673     {
    674       *passptr++ = passch;
    675       putchar(_CUPS_PASSCHAR);
    676     }
    677 
    678     fflush(stdout);
    679   }
    680 
    681   putchar('\n');
    682   fflush(stdout);
    683 
    684  /*
    685   * Cleanup...
    686   */
    687 
    688   SetConsoleMode(tty, mode);
    689 
    690  /*
    691   * Return the proper value...
    692   */
    693 
    694   if (passbytes == 1 && passptr > cg->password)
    695   {
    696     *passptr = '\0';
    697     return (cg->password);
    698   }
    699   else
    700   {
    701     memset(cg->password, 0, sizeof(cg->password));
    702     return (NULL);
    703   }
    704 
    705 #else
    706   int			tty;		/* /dev/tty - never read from stdin */
    707   struct termios	original,	/* Original input mode */
    708 			noecho;		/* No echo input mode */
    709   char			passch,		/* Current key press */
    710 			*passptr,	/* Pointer into password string */
    711 			*passend;	/* End of password string */
    712   ssize_t		passbytes;	/* Bytes read */
    713   _cups_globals_t	*cg = _cupsGlobals();
    714 					/* Thread globals */
    715 
    716 
    717  /*
    718   * Disable input echo and set raw input...
    719   */
    720 
    721   if ((tty = open("/dev/tty", O_RDONLY)) < 0)
    722     return (NULL);
    723 
    724   if (tcgetattr(tty, &original))
    725   {
    726     close(tty);
    727     return (NULL);
    728   }
    729 
    730   noecho = original;
    731   noecho.c_lflag &= (tcflag_t)~(ICANON | ECHO | ECHOE | ISIG);
    732   noecho.c_cc[VMIN]  = 1;
    733   noecho.c_cc[VTIME] = 0;
    734 
    735   if (tcsetattr(tty, TCSAFLUSH, &noecho))
    736   {
    737     close(tty);
    738     return (NULL);
    739   }
    740 
    741  /*
    742   * Display the prompt...
    743   */
    744 
    745   printf("%s ", prompt);
    746   fflush(stdout);
    747 
    748  /*
    749   * Read the password string from /dev/tty until we get interrupted or get a
    750   * carriage return or newline...
    751   */
    752 
    753   passptr = cg->password;
    754   passend = cg->password + sizeof(cg->password) - 1;
    755 
    756   while ((passbytes = read(tty, &passch, 1)) == 1)
    757   {
    758     if (passch == noecho.c_cc[VEOL] ||
    759 #  ifdef VEOL2
    760         passch == noecho.c_cc[VEOL2] ||
    761 #  endif /* VEOL2 */
    762         passch == 0x0A || passch == 0x0D)
    763     {
    764      /*
    765       * Enter/return...
    766       */
    767 
    768       break;
    769     }
    770     else if (passch == noecho.c_cc[VERASE] ||
    771              passch == 0x08 || passch == 0x7F)
    772     {
    773      /*
    774       * Backspace/delete (erase character)...
    775       */
    776 
    777       if (passptr > cg->password)
    778       {
    779         passptr --;
    780         fputs("\010 \010", stdout);
    781       }
    782       else
    783         putchar(0x07);
    784     }
    785     else if (passch == noecho.c_cc[VKILL])
    786     {
    787      /*
    788       * CTRL+U (erase line)
    789       */
    790 
    791       if (passptr > cg->password)
    792       {
    793 	while (passptr > cg->password)
    794 	{
    795           passptr --;
    796           fputs("\010 \010", stdout);
    797         }
    798       }
    799       else
    800         putchar(0x07);
    801     }
    802     else if (passch == noecho.c_cc[VINTR] || passch == noecho.c_cc[VQUIT] ||
    803              passch == noecho.c_cc[VEOF])
    804     {
    805      /*
    806       * CTRL+C, CTRL+D, or CTRL+Z...
    807       */
    808 
    809       passptr = cg->password;
    810       break;
    811     }
    812     else if ((passch & 255) < 0x20 || passptr >= passend)
    813       putchar(0x07);
    814     else
    815     {
    816       *passptr++ = passch;
    817       putchar(_CUPS_PASSCHAR);
    818     }
    819 
    820     fflush(stdout);
    821   }
    822 
    823   putchar('\n');
    824   fflush(stdout);
    825 
    826  /*
    827   * Cleanup...
    828   */
    829 
    830   tcsetattr(tty, TCSAFLUSH, &original);
    831   close(tty);
    832 
    833  /*
    834   * Return the proper value...
    835   */
    836 
    837   if (passbytes == 1 && passptr > cg->password)
    838   {
    839     *passptr = '\0';
    840     return (cg->password);
    841   }
    842   else
    843   {
    844     memset(cg->password, 0, sizeof(cg->password));
    845     return (NULL);
    846   }
    847 #endif /* WIN32 */
    848 }
    849 
    850 
    851 #ifdef HAVE_GSSAPI
    852 /*
    853  * '_cupsGSSServiceName()' - Get the GSS (Kerberos) service name.
    854  */
    855 
    856 const char *
    857 _cupsGSSServiceName(void)
    858 {
    859   _cups_globals_t *cg = _cupsGlobals();	/* Thread globals */
    860 
    861 
    862   if (!cg->gss_service_name[0])
    863     _cupsSetDefaults();
    864 
    865   return (cg->gss_service_name);
    866 }
    867 #endif /* HAVE_GSSAPI */
    868 
    869 
    870 /*
    871  * '_cupsSetDefaults()' - Set the default server, port, and encryption.
    872  */
    873 
    874 void
    875 _cupsSetDefaults(void)
    876 {
    877   cups_file_t	*fp;			/* File */
    878   const char	*home;			/* Home directory of user */
    879   char		filename[1024];		/* Filename */
    880   _cups_client_conf_t cc;		/* client.conf values */
    881   _cups_globals_t *cg = _cupsGlobals();	/* Pointer to library globals */
    882 
    883 
    884   DEBUG_puts("_cupsSetDefaults()");
    885 
    886  /*
    887   * Load initial client.conf values...
    888   */
    889 
    890   cups_init_client_conf(&cc);
    891 
    892  /*
    893   * Read the /etc/cups/client.conf and ~/.cups/client.conf files, if
    894   * present.
    895   */
    896 
    897   snprintf(filename, sizeof(filename), "%s/client.conf", cg->cups_serverroot);
    898   if ((fp = cupsFileOpen(filename, "r")) != NULL)
    899   {
    900     cups_read_client_conf(fp, &cc);
    901     cupsFileClose(fp);
    902   }
    903 
    904 #  ifdef HAVE_GETEUID
    905   if ((geteuid() == getuid() || !getuid()) && getegid() == getgid() && (home = getenv("HOME")) != NULL)
    906 #  elif !defined(WIN32)
    907   if (getuid() && (home = getenv("HOME")) != NULL)
    908 #  else
    909   if ((home = getenv("HOME")) != NULL)
    910 #  endif /* HAVE_GETEUID */
    911   {
    912    /*
    913     * Look for ~/.cups/client.conf...
    914     */
    915 
    916     snprintf(filename, sizeof(filename), "%s/.cups/client.conf", home);
    917     if ((fp = cupsFileOpen(filename, "r")) != NULL)
    918     {
    919       cups_read_client_conf(fp, &cc);
    920       cupsFileClose(fp);
    921     }
    922   }
    923 
    924  /*
    925   * Finalize things so every client.conf value is set...
    926   */
    927 
    928   cups_finalize_client_conf(&cc);
    929 
    930   if (cg->encryption == (http_encryption_t)-1)
    931     cg->encryption = cc.encryption;
    932 
    933   if (!cg->server[0] || !cg->ipp_port)
    934     cupsSetServer(cc.server_name);
    935 
    936   if (!cg->ipp_port)
    937     cups_set_default_ipp_port(cg);
    938 
    939   if (!cg->user[0])
    940     strlcpy(cg->user, cc.user, sizeof(cg->user));
    941 
    942 #ifdef HAVE_GSSAPI
    943   if (!cg->gss_service_name[0])
    944     strlcpy(cg->gss_service_name, cc.gss_service_name, sizeof(cg->gss_service_name));
    945 #endif /* HAVE_GSSAPI */
    946 
    947   if (cg->trust_first < 0)
    948     cg->trust_first = cc.trust_first;
    949 
    950   if (cg->any_root < 0)
    951     cg->any_root = cc.any_root;
    952 
    953   if (cg->expired_certs < 0)
    954     cg->expired_certs = cc.expired_certs;
    955 
    956   if (cg->validate_certs < 0)
    957     cg->validate_certs = cc.validate_certs;
    958 
    959 #ifdef HAVE_SSL
    960   _httpTLSSetOptions(cc.ssl_options | _HTTP_TLS_SET_DEFAULT);
    961 #endif /* HAVE_SSL */
    962 }
    963 
    964 
    965 #ifdef __APPLE__
    966 /*
    967  * 'cups_apple_get_boolean()' - Get a boolean setting from the CUPS preferences.
    968  */
    969 
    970 static int				/* O - 1 if set, 0 otherwise */
    971 cups_apple_get_boolean(
    972     CFStringRef key,			/* I - Key (name) */
    973     int         *value)			/* O - Boolean value */
    974 {
    975   Boolean	bval,			/* Preference value */
    976 		bval_set;		/* Value is set? */
    977 
    978 
    979   bval = CFPreferencesGetAppBooleanValue(key, kCUPSPrintingPrefs, &bval_set);
    980 
    981   if (bval_set)
    982     *value = (int)bval;
    983 
    984   return ((int)bval_set);
    985 }
    986 
    987 
    988 /*
    989  * 'cups_apple_get_string()' - Get a string setting from the CUPS preferences.
    990  */
    991 
    992 static int				/* O - 1 if set, 0 otherwise */
    993 cups_apple_get_string(
    994     CFStringRef key,			/* I - Key (name) */
    995     char        *value,			/* O - String value */
    996     size_t      valsize)		/* I - Size of value buffer */
    997 {
    998   CFStringRef	sval;			/* String value */
    999 
   1000 
   1001   if ((sval = CFPreferencesCopyAppValue(key, kCUPSPrintingPrefs)) != NULL)
   1002   {
   1003     Boolean result = CFStringGetCString(sval, value, (CFIndex)valsize, kCFStringEncodingUTF8);
   1004 
   1005     CFRelease(sval);
   1006 
   1007     if (result)
   1008       return (1);
   1009   }
   1010 
   1011   return (0);
   1012 }
   1013 #endif /* __APPLE__ */
   1014 
   1015 
   1016 /*
   1017  * 'cups_boolean_value()' - Convert a string to a boolean value.
   1018  */
   1019 
   1020 static int				/* O - Boolean value */
   1021 cups_boolean_value(const char *value)	/* I - String value */
   1022 {
   1023   return (!_cups_strcasecmp(value, "yes") || !_cups_strcasecmp(value, "on") || !_cups_strcasecmp(value, "true"));
   1024 }
   1025 
   1026 
   1027 /*
   1028  * 'cups_finalize_client_conf()' - Finalize client.conf values.
   1029  */
   1030 
   1031 static void
   1032 cups_finalize_client_conf(
   1033     _cups_client_conf_t *cc)		/* I - client.conf values */
   1034 {
   1035   const char	*value;			/* Environment variable */
   1036 
   1037 
   1038   if ((value = getenv("CUPS_TRUSTFIRST")) != NULL)
   1039     cc->trust_first = cups_boolean_value(value);
   1040 
   1041   if ((value = getenv("CUPS_ANYROOT")) != NULL)
   1042     cc->any_root = cups_boolean_value(value);
   1043 
   1044   if ((value = getenv("CUPS_ENCRYPTION")) != NULL)
   1045     cups_set_encryption(cc, value);
   1046 
   1047   if ((value = getenv("CUPS_EXPIREDCERTS")) != NULL)
   1048     cc->expired_certs = cups_boolean_value(value);
   1049 
   1050 #ifdef HAVE_GSSAPI
   1051   if ((value = getenv("CUPS_GSSSERVICENAME")) != NULL)
   1052     cups_set_gss_service_name(cc, value);
   1053 #endif /* HAVE_GSSAPI */
   1054 
   1055   if ((value = getenv("CUPS_SERVER")) != NULL)
   1056     cups_set_server_name(cc, value);
   1057 
   1058   if ((value = getenv("CUPS_USER")) != NULL)
   1059     cups_set_user(cc, value);
   1060 
   1061   if ((value = getenv("CUPS_VALIDATECERTS")) != NULL)
   1062     cc->validate_certs = cups_boolean_value(value);
   1063 
   1064  /*
   1065   * Then apply defaults for those values that haven't been set...
   1066   */
   1067 
   1068   if (cc->trust_first < 0)
   1069     cc->trust_first = 1;
   1070 
   1071   if (cc->any_root < 0)
   1072     cc->any_root = 1;
   1073 
   1074   if (cc->encryption == (http_encryption_t)-1)
   1075     cc->encryption = HTTP_ENCRYPTION_IF_REQUESTED;
   1076 
   1077   if (cc->expired_certs < 0)
   1078     cc->expired_certs = 0;
   1079 
   1080 #ifdef HAVE_GSSAPI
   1081   if (!cc->gss_service_name[0])
   1082     cups_set_gss_service_name(cc, CUPS_DEFAULT_GSSSERVICENAME);
   1083 #endif /* HAVE_GSSAPI */
   1084 
   1085   if (!cc->server_name[0])
   1086   {
   1087 #ifdef CUPS_DEFAULT_DOMAINSOCKET
   1088    /*
   1089     * If we are compiled with domain socket support, only use the
   1090     * domain socket if it exists and has the right permissions...
   1091     */
   1092 
   1093     if (!access(CUPS_DEFAULT_DOMAINSOCKET, R_OK))
   1094       cups_set_server_name(cc, CUPS_DEFAULT_DOMAINSOCKET);
   1095     else
   1096 #endif /* CUPS_DEFAULT_DOMAINSOCKET */
   1097       cups_set_server_name(cc, "localhost");
   1098   }
   1099 
   1100   if (!cc->user[0])
   1101   {
   1102 #ifdef WIN32
   1103    /*
   1104     * Get the current user name from the OS...
   1105     */
   1106 
   1107     DWORD	size;			/* Size of string */
   1108 
   1109     size = sizeof(cc->user);
   1110     if (!GetUserName(cc->user, &size))
   1111 #else
   1112    /*
   1113     * Try the USER environment variable as the default username...
   1114     */
   1115 
   1116     const char *envuser = getenv("USER");
   1117 					/* Default username */
   1118     struct passwd *pw = NULL;		/* Account information */
   1119 
   1120     if (envuser)
   1121     {
   1122      /*
   1123       * Validate USER matches the current UID, otherwise don't allow it to
   1124       * override things...  This makes sure that printing after doing su
   1125       * or sudo records the correct username.
   1126       */
   1127 
   1128       if ((pw = getpwnam(envuser)) != NULL && pw->pw_uid != getuid())
   1129 	pw = NULL;
   1130     }
   1131 
   1132     if (!pw)
   1133       pw = getpwuid(getuid());
   1134 
   1135     if (pw)
   1136       strlcpy(cc->user, pw->pw_name, sizeof(cc->user));
   1137     else
   1138 #endif /* WIN32 */
   1139     {
   1140      /*
   1141       * Use the default "unknown" user name...
   1142       */
   1143 
   1144       strlcpy(cc->user, "unknown", sizeof(cc->user));
   1145     }
   1146   }
   1147 
   1148   if (cc->validate_certs < 0)
   1149     cc->validate_certs = 0;
   1150 }
   1151 
   1152 
   1153 /*
   1154  * 'cups_init_client_conf()' - Initialize client.conf values.
   1155  */
   1156 
   1157 static void
   1158 cups_init_client_conf(
   1159     _cups_client_conf_t *cc)		/* I - client.conf values */
   1160 {
   1161  /*
   1162   * Clear all values to "not set"...
   1163   */
   1164 
   1165   memset(cc, 0, sizeof(_cups_client_conf_t));
   1166 
   1167   cc->encryption     = (http_encryption_t)-1;
   1168   cc->trust_first    = -1;
   1169   cc->any_root       = -1;
   1170   cc->expired_certs  = -1;
   1171   cc->validate_certs = -1;
   1172 
   1173  /*
   1174   * Load settings from the org.cups.PrintingPrefs plist (which trump
   1175   * everything...)
   1176   */
   1177 
   1178 #if defined(__APPLE__) && defined(HAVE_SSL)
   1179   char	sval[1024];			/* String value */
   1180   int	bval;				/* Boolean value */
   1181 
   1182   if (cups_apple_get_boolean(kAllowAnyRootKey, &bval))
   1183     cc->any_root = bval;
   1184 
   1185   if (cups_apple_get_boolean(kAllowExpiredCertsKey, &bval))
   1186     cc->expired_certs = bval;
   1187 
   1188   if (cups_apple_get_string(kEncryptionKey, sval, sizeof(sval)))
   1189     cups_set_encryption(cc, sval);
   1190 
   1191   if (cups_apple_get_string(kSSLOptionsKey, sval, sizeof(sval)))
   1192     cups_set_ssl_options(cc, sval);
   1193 
   1194   if (cups_apple_get_boolean(kTrustOnFirstUseKey, &bval))
   1195     cc->trust_first = bval;
   1196 
   1197   if (cups_apple_get_boolean(kValidateCertsKey, &bval))
   1198     cc->validate_certs = bval;
   1199 #endif /* __APPLE__ && HAVE_SSL */
   1200 }
   1201 
   1202 
   1203 /*
   1204  * 'cups_read_client_conf()' - Read a client.conf file.
   1205  */
   1206 
   1207 static void
   1208 cups_read_client_conf(
   1209     cups_file_t         *fp,		/* I - File to read */
   1210     _cups_client_conf_t *cc)		/* I - client.conf values */
   1211 {
   1212   int	linenum;			/* Current line number */
   1213   char	line[1024],			/* Line from file */
   1214         *value;				/* Pointer into line */
   1215 
   1216 
   1217  /*
   1218   * Read from the file...
   1219   */
   1220 
   1221   linenum = 0;
   1222   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
   1223   {
   1224     if (!_cups_strcasecmp(line, "Encryption") && value)
   1225       cups_set_encryption(cc, value);
   1226 #ifndef __APPLE__
   1227    /*
   1228     * The ServerName directive is not supported on macOS due to app
   1229     * sandboxing restrictions, i.e. not all apps request network access.
   1230     */
   1231     else if (!_cups_strcasecmp(line, "ServerName") && value)
   1232       cups_set_server_name(cc, value);
   1233 #endif /* !__APPLE__ */
   1234     else if (!_cups_strcasecmp(line, "User") && value)
   1235       cups_set_user(cc, value);
   1236     else if (!_cups_strcasecmp(line, "TrustOnFirstUse") && value)
   1237       cc->trust_first = cups_boolean_value(value);
   1238     else if (!_cups_strcasecmp(line, "AllowAnyRoot") && value)
   1239       cc->any_root = cups_boolean_value(value);
   1240     else if (!_cups_strcasecmp(line, "AllowExpiredCerts") &&
   1241              value)
   1242       cc->expired_certs = cups_boolean_value(value);
   1243     else if (!_cups_strcasecmp(line, "ValidateCerts") && value)
   1244       cc->validate_certs = cups_boolean_value(value);
   1245 #ifdef HAVE_GSSAPI
   1246     else if (!_cups_strcasecmp(line, "GSSServiceName") && value)
   1247       cups_set_gss_service_name(cc, value);
   1248 #endif /* HAVE_GSSAPI */
   1249 #ifdef HAVE_SSL
   1250     else if (!_cups_strcasecmp(line, "SSLOptions") && value)
   1251       cups_set_ssl_options(cc, value);
   1252 #endif /* HAVE_SSL */
   1253   }
   1254 }
   1255 
   1256 
   1257 /*
   1258  * 'cups_set_default_ipp_port()' - Set the default IPP port value.
   1259  */
   1260 
   1261 static void
   1262 cups_set_default_ipp_port(
   1263     _cups_globals_t *cg)		/* I - Global data */
   1264 {
   1265   const char	*ipp_port;		/* IPP_PORT environment variable */
   1266 
   1267 
   1268   if ((ipp_port = getenv("IPP_PORT")) != NULL)
   1269   {
   1270     if ((cg->ipp_port = atoi(ipp_port)) <= 0)
   1271       cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
   1272   }
   1273   else
   1274     cg->ipp_port = CUPS_DEFAULT_IPP_PORT;
   1275 }
   1276 
   1277 /*
   1278  * 'cups_set_encryption()' - Set the Encryption value.
   1279  */
   1280 
   1281 static void
   1282 cups_set_encryption(
   1283     _cups_client_conf_t *cc,		/* I - client.conf values */
   1284     const char          *value)		/* I - Value */
   1285 {
   1286   if (!_cups_strcasecmp(value, "never"))
   1287     cc->encryption = HTTP_ENCRYPTION_NEVER;
   1288   else if (!_cups_strcasecmp(value, "always"))
   1289     cc->encryption = HTTP_ENCRYPTION_ALWAYS;
   1290   else if (!_cups_strcasecmp(value, "required"))
   1291     cc->encryption = HTTP_ENCRYPTION_REQUIRED;
   1292   else
   1293     cc->encryption = HTTP_ENCRYPTION_IF_REQUESTED;
   1294 }
   1295 
   1296 
   1297 /*
   1298  * 'cups_set_gss_service_name()' - Set the GSSServiceName value.
   1299  */
   1300 
   1301 #ifdef HAVE_GSSAPI
   1302 static void
   1303 cups_set_gss_service_name(
   1304     _cups_client_conf_t *cc,		/* I - client.conf values */
   1305     const char          *value)		/* I - Value */
   1306 {
   1307   strlcpy(cc->gss_service_name, value, sizeof(cc->gss_service_name));
   1308 }
   1309 #endif /* HAVE_GSSAPI */
   1310 
   1311 
   1312 /*
   1313  * 'cups_set_server_name()' - Set the ServerName value.
   1314  */
   1315 
   1316 static void
   1317 cups_set_server_name(
   1318     _cups_client_conf_t *cc,		/* I - client.conf values */
   1319     const char          *value)		/* I - Value */
   1320 {
   1321   strlcpy(cc->server_name, value, sizeof(cc->server_name));
   1322 }
   1323 
   1324 
   1325 /*
   1326  * 'cups_set_ssl_options()' - Set the SSLOptions value.
   1327  */
   1328 
   1329 #ifdef HAVE_SSL
   1330 static void
   1331 cups_set_ssl_options(
   1332     _cups_client_conf_t *cc,		/* I - client.conf values */
   1333     const char          *value)		/* I - Value */
   1334 {
   1335  /*
   1336   * SSLOptions [AllowRC4] [AllowSSL3] [AllowDH] [DenyTLS1.0] [None]
   1337   */
   1338 
   1339   int	options = _HTTP_TLS_NONE;	/* SSL/TLS options */
   1340   char	temp[256],			/* Copy of value */
   1341 	*start,				/* Start of option */
   1342 	*end;				/* End of option */
   1343 
   1344 
   1345   strlcpy(temp, value, sizeof(temp));
   1346 
   1347   for (start = temp; *start; start = end)
   1348   {
   1349    /*
   1350     * Find end of keyword...
   1351     */
   1352 
   1353     end = start;
   1354     while (*end && !_cups_isspace(*end))
   1355       end ++;
   1356 
   1357     if (*end)
   1358       *end++ = '\0';
   1359 
   1360    /*
   1361     * Compare...
   1362     */
   1363 
   1364     if (!_cups_strcasecmp(start, "AllowRC4"))
   1365       options |= _HTTP_TLS_ALLOW_RC4;
   1366     else if (!_cups_strcasecmp(start, "AllowSSL3"))
   1367       options |= _HTTP_TLS_ALLOW_SSL3;
   1368     else if (!_cups_strcasecmp(start, "AllowDH"))
   1369       options |= _HTTP_TLS_ALLOW_DH;
   1370     else if (!_cups_strcasecmp(start, "DenyCBC"))
   1371       options |= _HTTP_TLS_DENY_CBC;
   1372     else if (!_cups_strcasecmp(start, "DenyTLS1.0"))
   1373       options |= _HTTP_TLS_DENY_TLS10;
   1374     else if (!_cups_strcasecmp(start, "None"))
   1375       options = _HTTP_TLS_NONE;
   1376   }
   1377 
   1378   cc->ssl_options = options;
   1379 
   1380   DEBUG_printf(("4cups_set_ssl_options(cc=%p, value=\"%s\") options=%x", (void *)cc, value, options));
   1381 }
   1382 #endif /* HAVE_SSL */
   1383 
   1384 
   1385 /*
   1386  * 'cups_set_user()' - Set the User value.
   1387  */
   1388 
   1389 static void
   1390 cups_set_user(
   1391     _cups_client_conf_t *cc,		/* I - client.conf values */
   1392     const char          *value)		/* I - Value */
   1393 {
   1394   strlcpy(cc->user, value, sizeof(cc->user));
   1395 }
   1396