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 */
cupsEncryption(void)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 */
cupsGetPassword(const char * prompt)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 */
cupsGetPassword2(const char * prompt,http_t * http,const char * method,const char * resource)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 */
cupsServer(void)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
cupsSetClientCertCB(cups_client_cert_cb_t cb,void * user_data)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) */
cupsSetCredentials(cups_array_t * credentials)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
cupsSetEncryption(http_encryption_t e)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
cupsSetPasswordCB(cups_password_cb_t cb)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
cupsSetPasswordCB2(cups_password_cb2_t cb,void * user_data)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
cupsSetServer(const char * server)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
cupsSetServerCertCB(cups_server_cert_cb_t cb,void * user_data)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
cupsSetUser(const char * user)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
cupsSetUserAgent(const char * user_agent)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 */
cupsUser(void)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 */
cupsUserAgent(void)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 */
_cupsGetPassword(const char * prompt)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 *
_cupsGSSServiceName(void)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
_cupsSetDefaults(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 */
cups_apple_get_boolean(CFStringRef key,int * value)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 */
cups_apple_get_string(CFStringRef key,char * value,size_t valsize)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 */
cups_boolean_value(const char * 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
cups_finalize_client_conf(_cups_client_conf_t * cc)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
cups_init_client_conf(_cups_client_conf_t * cc)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
cups_read_client_conf(cups_file_t * fp,_cups_client_conf_t * cc)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
cups_set_default_ipp_port(_cups_globals_t * cg)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
cups_set_encryption(_cups_client_conf_t * cc,const char * value)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
cups_set_gss_service_name(_cups_client_conf_t * cc,const char * value)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
cups_set_server_name(_cups_client_conf_t * cc,const char * value)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
cups_set_ssl_options(_cups_client_conf_t * cc,const char * value)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
cups_set_user(_cups_client_conf_t * cc,const char * value)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