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