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
54typedef 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__
79static int	cups_apple_get_boolean(CFStringRef key, int *value);
80static int	cups_apple_get_string(CFStringRef key, char *value, size_t valsize);
81#endif /* __APPLE__ */
82static int	cups_boolean_value(const char *value);
83static void	cups_finalize_client_conf(_cups_client_conf_t *cc);
84static void	cups_init_client_conf(_cups_client_conf_t *cc);
85static void	cups_read_client_conf(cups_file_t *fp, _cups_client_conf_t *cc);
86static void	cups_set_default_ipp_port(_cups_globals_t *cg);
87static void	cups_set_encryption(_cups_client_conf_t *cc, const char *value);
88#ifdef HAVE_GSSAPI
89static void	cups_set_gss_service_name(_cups_client_conf_t *cc, const char *value);
90#endif /* HAVE_GSSAPI */
91static void	cups_set_server_name(_cups_client_conf_t *cc, const char *value);
92#ifdef HAVE_SSL
93static void	cups_set_ssl_options(_cups_client_conf_t *cc, const char *value);
94#endif /* HAVE_SSL */
95static 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
112http_encryption_t			/* O - Encryption settings */
113cupsEncryption(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
139const char *				/* O - Password */
140cupsGetPassword(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
164const char *				/* O - Password */
165cupsGetPassword2(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
197const char *				/* O - Server name */
198cupsServer(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
222void
223cupsSetClientCertCB(
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
246int					/* O - Status of call (0 = success) */
247cupsSetCredentials(
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
278void
279cupsSetEncryption(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
306void
307cupsSetPasswordCB(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
336void
337cupsSetPasswordCB2(
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
367void
368cupsSetServer(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
441void
442cupsSetServerCertCB(
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
464void
465cupsSetUser(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
486void
487cupsSetUserAgent(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
542const char *				/* O - User name */
543cupsUser(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
561const char *				/* O - User-Agent string */
562cupsUserAgent(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
578const 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
856const 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
874void
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
970static int				/* O - 1 if set, 0 otherwise */
971cups_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
992static int				/* O - 1 if set, 0 otherwise */
993cups_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
1020static int				/* O - Boolean value */
1021cups_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
1031static void
1032cups_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
1157static void
1158cups_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
1207static void
1208cups_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
1261static void
1262cups_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
1281static void
1282cups_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
1302static void
1303cups_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
1316static void
1317cups_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
1330static void
1331cups_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
1389static void
1390cups_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