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