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