2 Unix SMB/CIFS implementation.
3 SMB backend for the Common UNIX Printing System ("CUPS")
4 Copyright 1999 by Easy Software Products
5 Copyright Andrew Tridgell 1994-1998
6 Copyright Andrew Bartlett 2002
7 Copyright Rodrigo Fernandez-Vizarra 2005
8 Copyright James Peach 2008
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 Starting with CUPS 1.3, Kerberos support is provided by cupsd including
28 the forwarding of user credentials via the authenticated session between
29 user and server and the KRB5CCNAME environment variable which will point
30 to a temporary file or an in-memory representation depending on the version
31 of Kerberos you use. As a result, all of the ticket code that used to
32 live here has been removed, and we depend on the user session (if you
33 run smbspool by hand) or cupsd to provide the necessary Kerberos info.
35 Also, the AUTH_USERNAME and AUTH_PASSWORD environment variables provide
36 for per-job authentication for non-Kerberized printing. We use those
37 if there is no username and password specified in the device URI.
39 Finally, if we have an authentication failure we return exit code 2
40 which tells CUPS to hold the job for authentication and bug the user
41 to get the necessary credentials.
44 #define MAX_RETRY_CONNECT 3
57 static int get_exit_code(struct cli_state *cli, NTSTATUS nt_status);
58 static void list_devices(void);
59 static struct cli_state *smb_complete_connection(const char *, const char *,int , const char *, const char *, const char *, const char *, int, int *need_auth);
60 static struct cli_state *smb_connect(const char *, const char *, int, const char *, const char *, const char *, const char *, int *need_auth);
61 static int smb_print(struct cli_state *, char *, FILE *);
62 static char * uri_unescape_alloc(const char *);
64 static bool smb_encrypt;
68 * 'main()' - Main entry for SMB backend.
71 int /* O - Exit status */
72 main(int argc, /* I - Number of command-line arguments */
73 char *argv[]) /* I - Command-line arguments */
75 int i; /* Looping var */
76 int copies; /* Number of copies */
77 int port; /* Port number */
78 char uri[1024], /* URI */
79 *sep, /* Pointer to separator */
80 *tmp, *tmp2, /* Temp pointers to do escaping */
81 *password; /* Password */
82 char *username, /* Username */
83 *server, /* Server name */
84 *printer; /* Printer name */
85 const char *workgroup; /* Workgroup */
86 FILE *fp; /* File to print */
87 int status=1; /* Status of LPD job */
88 struct cli_state *cli; /* SMB interface */
92 TALLOC_CTX *frame = talloc_stackframe();
96 /* we expect the URI in argv[0]. Detect the case where it is in argv[1] and cope */
97 if (argc > 2 && strncmp(argv[0],"smb://", 6) && !strncmp(argv[1],"smb://", 6)) {
105 * NEW! In CUPS 1.1 the backends are run with no arguments to list the
106 * available devices. These can be devices served by this backend
107 * or any other backends (i.e. you can have an SNMP backend that
108 * is only used to enumerate the available network printers... :)
116 if (argc < 6 || argc > 7)
118 fprintf(stderr, "Usage: %s [DEVICE_URI] job-id user title copies options [file]\n",
120 fputs(" The DEVICE_URI environment variable can also contain the\n", stderr);
121 fputs(" destination printer:\n", stderr);
123 fputs(" smb://[username:password@][workgroup/]server[:port]/printer\n", stderr);
128 * If we have 7 arguments, print the file named on the command-line.
129 * Otherwise, print data from stdin...
136 * Print from Copy stdin to a temporary file...
142 else if ((fp = fopen(argv[6], "rb")) == NULL)
144 perror("ERROR: Unable to open print file");
148 copies = atoi(argv[4]);
154 dev_uri = getenv("DEVICE_URI");
156 strncpy(uri, dev_uri, sizeof(uri) - 1);
157 else if (strncmp(argv[0], "smb://", 6) == 0)
158 strncpy(uri, argv[0], sizeof(uri) - 1);
161 fputs("ERROR: No device URI found in DEVICE_URI environment variable or argv[0] !\n", stderr);
165 uri[sizeof(uri) - 1] = '\0';
168 * Extract the destination from the URI...
171 if ((sep = strrchr_m(uri, '@')) != NULL)
176 /* username is in tmp */
181 * Extract password as needed...
184 if ((tmp2 = strchr_m(tmp, ':')) != NULL) {
186 password = uri_unescape_alloc(tmp2);
190 username = uri_unescape_alloc(tmp);
194 if ((username = getenv("AUTH_USERNAME")) == NULL)
197 if ((password = getenv("AUTH_PASSWORD")) == NULL)
205 if ((sep = strchr_m(tmp, '/')) == NULL)
207 fputs("ERROR: Bad URI - need printer name!\n", stderr);
214 if ((sep = strchr_m(tmp2, '/')) != NULL)
217 * Convert to smb://[username:password@]workgroup/server/printer...
222 workgroup = uri_unescape_alloc(tmp);
223 server = uri_unescape_alloc(tmp2);
224 printer = uri_unescape_alloc(sep);
228 server = uri_unescape_alloc(tmp);
229 printer = uri_unescape_alloc(tmp2);
232 if ((sep = strrchr_m(server, ':')) != NULL)
242 * Setup the SAMBA server state...
245 setup_logging("smbspool", True);
247 lp_set_in_client(True); /* Make sure that we tell lp_load we are */
251 if (!lp_load(get_dyn_CONFIGFILE(), True, False, False, True))
253 fprintf(stderr, "ERROR: Can't load %s - run testparm to debug it\n", get_dyn_CONFIGFILE());
257 if (workgroup == NULL)
258 workgroup = lp_workgroup();
264 if ((cli = smb_connect(workgroup, server, port, printer, username, password, argv[2], &need_auth)) == NULL)
268 else if (getenv("CLASS") == NULL)
270 fprintf(stderr, "ERROR: Unable to connect to CIFS host, will retry in 60 seconds...\n");
276 fprintf(stderr, "ERROR: Unable to connect to CIFS host, trying next printer...\n");
281 while ((cli == NULL) && (tries < MAX_RETRY_CONNECT));
284 fprintf(stderr, "ERROR: Unable to connect to CIFS host after (tried %d times)\n", tries);
289 * Now that we are connected to the server, ignore SIGTERM so that we
290 * can finish out any page data the driver sends (e.g. to eject the
291 * current page... Only ignore SIGTERM if we are printing data from
292 * stdin (otherwise you can't cancel raw jobs...)
296 CatchSignal(SIGTERM, SIG_IGN);
302 for (i = 0; i < copies; i ++)
303 if ((status = smb_print(cli, argv[3] /* title */, fp)) != 0)
309 * Return the queue status...
320 * 'get_exit_code()' - Get the backend exit code based on the current error.
324 get_exit_code(struct cli_state *cli,
328 static const NTSTATUS auth_errors[] =
329 { /* List of NTSTATUS errors that are considered authentication errors */
330 NT_STATUS_ACCESS_DENIED, NT_STATUS_ACCESS_VIOLATION,
331 NT_STATUS_SHARING_VIOLATION, NT_STATUS_PRIVILEGE_NOT_HELD,
332 NT_STATUS_INVALID_ACCOUNT_NAME, NT_STATUS_NO_SUCH_USER,
333 NT_STATUS_WRONG_PASSWORD, NT_STATUS_LOGON_FAILURE,
334 NT_STATUS_ACCOUNT_RESTRICTION, NT_STATUS_INVALID_LOGON_HOURS,
335 NT_STATUS_PASSWORD_EXPIRED, NT_STATUS_ACCOUNT_DISABLED
339 fprintf(stderr, "DEBUG: get_exit_code(cli=%p, nt_status=%x)\n", cli, nt_status);
341 for (i = 0; i < (int)(sizeof(auth_errors) / sizeof(auth_errors[0])); i ++)
342 if (NT_STATUS_V(nt_status) == NT_STATUS_V(auth_errors[i]))
346 if (cli->use_kerberos || (cli->capabilities & CAP_EXTENDED_SECURITY))
347 fputs("ATTR: auth-info-required=negotiate\n", stderr);
349 fputs("ATTR: auth-info-required=username,password\n", stderr);
353 * 2 = authentication required...
368 * 'list_devices()' - List the available printers seen on the network...
375 * Eventually, search the local workgroup for available hosts and printers.
378 puts("network smb \"Unknown\" \"Windows Printer via SAMBA\"");
382 static struct cli_state
383 *smb_complete_connection(const char *myname,
386 const char *username,
387 const char *password,
388 const char *workgroup,
393 struct cli_state *cli; /* New connection */
396 /* Start the SMB connection */
398 nt_status = cli_start_connection( &cli, myname, server, NULL, port,
399 Undefined, flags, NULL);
400 if (!NT_STATUS_IS_OK(nt_status))
402 fprintf(stderr,"ERROR: Connection failed: %s\n", nt_errstr(nt_status));
406 /* We pretty much guarantee password must be valid or a pointer
413 nt_status = cli_session_setup(cli, username,
414 password, strlen(password)+1,
415 password, strlen(password)+1,
417 if (!NT_STATUS_IS_OK(nt_status))
419 fprintf(stderr,"ERROR: Session setup failed: %s\n", nt_errstr(nt_status));
421 if (get_exit_code(cli, nt_status) == 2)
429 if (!cli_send_tconX(cli, share, "?????", password, strlen(password)+1))
431 fprintf(stderr, "ERROR: Tree connect failed (%s)\n", cli_errstr(cli));
433 if (get_exit_code(cli, cli_nt_error(cli)) == 2)
442 /* Need to work out how to specify this on the URL. */
445 if (!cli_cm_force_encryption(cli,
451 fprintf(stderr, "ERROR: encryption setup failed\n");
462 * 'smb_connect()' - Return a connection to a server.
465 static struct cli_state * /* O - SMB connection */
466 smb_connect(const char *workgroup, /* I - Workgroup */
467 const char *server, /* I - Server */
468 const int port, /* I - Port */
469 const char *share, /* I - Printer */
470 const char *username, /* I - Username */
471 const char *password, /* I - Password */
472 const char *jobusername, /* I - User who issued the print job */
473 int *need_auth) /* O - Need authentication? */
475 struct cli_state *cli; /* New connection */
476 char *myname = NULL; /* Client name */
480 * Get the names and addresses of the client and server...
483 myname = get_myname(talloc_tos());
488 /* See if we have a username first. This is for backwards compatible
489 behavior with 3.0.14a */
491 if (username && *username && !getenv("KRB5CCNAME"))
493 cli = smb_complete_connection(myname, server, port, username,
494 password, workgroup, share, 0, need_auth);
497 fputs("DEBUG: Connected with username/password...\n", stderr);
503 * Try to use the user kerberos credentials (if any) to authenticate
505 cli = smb_complete_connection(myname, server, port, jobusername, "",
507 CLI_FULL_CONNECTION_USE_KERBEROS, need_auth);
511 fputs("DEBUG: Connected using Kerberos...\n", stderr);
515 /* give a chance for a passwordless NTLMSSP session setup */
516 pwd = getpwuid(geteuid());
521 cli = smb_complete_connection(myname, server, port, pwd->pw_name, "",
522 workgroup, share, 0, need_auth);
526 fputs("DEBUG: Connected with NTLMSSP...\n", stderr);
531 * last try. Use anonymous authentication
534 cli = smb_complete_connection(myname, server, port, "", "",
535 workgroup, share, 0, need_auth);
537 * Return the new connection...
545 * 'smb_print()' - Queue a job for printing using the SMB protocol.
548 static int /* O - 0 = success, non-0 = failure */
549 smb_print(struct cli_state *cli, /* I - SMB connection */
550 char *title, /* I - Title/job name */
551 FILE *fp) /* I - File to print */
553 int fnum; /* File number */
554 int nbytes, /* Number of bytes read */
555 tbytes; /* Total bytes read */
556 char buffer[8192], /* Buffer for copy */
557 *ptr; /* Pointer into tile */
561 * Sanitize the title...
564 for (ptr = title; *ptr; ptr ++)
565 if (!isalnum((int)*ptr) && !isspace((int)*ptr))
569 * Open the printer device...
572 if ((fnum = cli_open(cli, title, O_RDWR | O_CREAT | O_TRUNC, DENY_NONE)) == -1)
574 fprintf(stderr, "ERROR: %s opening remote spool %s\n",
575 cli_errstr(cli), title);
576 return (get_exit_code(cli, cli_nt_error(cli)));
580 * Copy the file to the printer...
588 while ((nbytes = fread(buffer, 1, sizeof(buffer), fp)) > 0)
590 if (cli_write(cli, fnum, 0, buffer, tbytes, nbytes) != nbytes)
592 int status = get_exit_code(cli, cli_nt_error(cli));
594 fprintf(stderr, "ERROR: Error writing spool: %s\n", cli_errstr(cli));
595 fprintf(stderr, "DEBUG: Returning status %d...\n", status);
596 cli_close(cli, fnum);
604 if (!cli_close(cli, fnum))
606 fprintf(stderr, "ERROR: %s closing remote spool %s\n",
607 cli_errstr(cli), title);
608 return (get_exit_code(cli, cli_nt_error(cli)));
614 static char *uri_unescape_alloc(const char *uritok)
618 ret = (char *)SMB_STRDUP(uritok);
619 if (!ret) return NULL;
621 rfc1738_unescape(ret);