aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-03-23 23:40:18 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-03-23 23:40:18 +0000
commita79428998d76c1758ca12546e5db945a0cd64518 (patch)
tree88a26fd659f414fbdfeb9208be708d7339a2bf02
parentc93b162248892265eae5f54e9ee409074dfa08c5 (diff)
downloadbusybox-w32-a79428998d76c1758ca12546e5db945a0cd64518.tar.gz
busybox-w32-a79428998d76c1758ca12546e5db945a0cd64518.tar.bz2
busybox-w32-a79428998d76c1758ca12546e5db945a0cd64518.zip
lpd: maintainer's update: now with spool helpers
function old new delta exec_helper - 227 +227 sane 66 117 +51 packed_usage 23806 23828 +22 lpd_main 486 504 +18 ------------------------------------------------------------------------------ (add/remove: 1/0 grow/shrink: 3/0 up/down: 318/0) Total: 318 bytes
-rw-r--r--include/usage.h4
-rw-r--r--printutils/lpd.c147
2 files changed, 133 insertions, 18 deletions
diff --git a/include/usage.h b/include/usage.h
index 54b6a4d99..450f80164 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -2108,10 +2108,10 @@
2108 "losetup -f will show the first loop free loop device\n\n" 2108 "losetup -f will show the first loop free loop device\n\n"
2109 2109
2110#define lpd_trivial_usage \ 2110#define lpd_trivial_usage \
2111 "SPOOLDIR" 2111 "SPOOLDIR [HELPER [ARGS...]]"
2112#define lpd_full_usage \ 2112#define lpd_full_usage \
2113 "Example:" \ 2113 "Example:" \
2114 "\n tcpsvd -E 0 515 softlimit -m 99999 lpd /var/spool" 2114 "\n tcpsvd -E 0 515 softlimit -m 999999 lpd /var/spool ./print"
2115 2115
2116#define lpq_trivial_usage \ 2116#define lpq_trivial_usage \
2117 "[-P queue[@host[:port]]] [-U USERNAME] [-d JOBID...] [-fs]" 2117 "[-P queue[@host[:port]]] [-U USERNAME] [-d JOBID...] [-fs]"
diff --git a/printutils/lpd.c b/printutils/lpd.c
index 49e3fd744..45ad6d7e5 100644
--- a/printutils/lpd.c
+++ b/printutils/lpd.c
@@ -6,10 +6,123 @@
6 * 6 *
7 * Licensed under GPLv2, see file LICENSE in this tarball for details. 7 * Licensed under GPLv2, see file LICENSE in this tarball for details.
8 */ 8 */
9
10/*
11 * A typical usage of BB lpd looks as follows:
12 * # tcpsvd -E 0 515 lpd SPOOLDIR [HELPER-PROG [ARGS...]]
13 *
14 * This means a network listener is started on port 515 (default for LP protocol).
15 * When a client connection is made (via lpr) lpd first change its working directory to SPOOLDIR.
16 *
17 * SPOOLDIR is the spool directory which contains printing queues
18 * and should have the following structure:
19 *
20 * SPOOLDIR/
21 * <queue1>
22 * ...
23 * <queueN>
24 *
25 * <queueX> can be of two types:
26 * A. a printer character device or an ordinary file a link to such;
27 * B. a directory.
28 *
29 * In case A lpd just dumps the data it receives from client (lpr) to the
30 * end of queue file/device. This is non-spooling mode.
31 *
32 * In case B lpd enters spooling mode. It reliably saves client data along with control info
33 * in two unique files under the queue directory. These files are named dfAXXXHHHH and cfAXXXHHHH,
34 * where XXX is the job number and HHHH is the client hostname. Unless a printing helper application
35 * is specified lpd is done at this point.
36 *
37 * If HELPER-PROG (with optional arguments) is specified then lpd continues to process client data:
38 * 1. it reads and parses control file (cfA...). The parse process results in setting environment
39 * variables whose values were passed in control file; when parsing is complete, lpd deletes
40 * control file.
41 * 2. it spawns specified helper application. It is then the helper application who is responsible
42 * for both actual printing and deleting processed data file.
43 *
44 * A good lpr passes control files which when parsed provide the following variables:
45 * $H = host which issues the job
46 * $P = user who prints
47 * $C = class of printing (what is printed on banner page)
48 * $J = the name of the job
49 * $L = print banner page
50 * $M = the user to whom a mail should be sent if a problem occurs
51 * $l = name of datafile ("dfAxxx") - file whose content are to be printed
52 *
53 * Thus, a typical helper can be something like this:
54 * #!/bin/sh
55 * cat "$l" >/dev/lp0
56 * mv -f "$l" save/
57 *
58 */
9#include "libbb.h" 59#include "libbb.h"
10 60
11// TODO: xmalloc_reads is vulnerable to remote OOM attack! 61// TODO: xmalloc_reads is vulnerable to remote OOM attack!
12 62
63// strip argument of bad chars
64static char *sane(char *str)
65{
66 char *s = str;
67 char *p = s;
68 while (*s) {
69 if (isalnum(*s) || '-' == *s) {
70 *p++ = *s;
71 }
72 s++;
73 }
74 *p = '\0';
75 return str;
76}
77
78static void exec_helper(const char *fname, char **argv)
79{
80 char *p, *q, *file;
81 char *our_env[12];
82 int env_idx;
83
84 // read control file
85 file = q = xmalloc_open_read_close(fname, NULL);
86 // delete control file
87 unlink(fname);
88 // parse control file by "\n"
89 env_idx = 0;
90 while ((p = strchr(q, '\n')) != NULL
91 && isalpha(*q)
92 && env_idx < ARRAY_SIZE(our_env)
93 ) {
94 *p++ = '\0';
95 // here q is a line of <SYM><VALUE>
96 // let us set environment string <SYM>=<VALUE>
97 // N.B. setenv is leaky!
98 // We have to use putenv(malloced_str),
99 // and unsetenv+free (in parent)
100 our_env[env_idx] = xasprintf("%c=%s", *q, q+1);
101 putenv(our_env[env_idx]);
102 env_idx++;
103 // next line, plz!
104 q = p;
105 }
106
107 if (vfork() == 0) {
108 // CHILD
109 // we are the helper. we wanna be silent
110 // this call reopens stdio fds to "/dev/null"
111 // (no daemonization is done)
112 bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
113 BB_EXECVP(*argv, argv);
114 _exit(127);
115 }
116
117 // PARENT (or vfork error)
118 // clean up...
119 free(file);
120 while (--env_idx >= 0) {
121 *strchrnul(our_env[env_idx], '=') = '\0';
122 unsetenv(our_env[env_idx]);
123 }
124}
125
13int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; 126int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
14int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[]) 127int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[])
15{ 128{
@@ -27,22 +140,16 @@ int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[])
27 return EXIT_FAILURE; 140 return EXIT_FAILURE;
28 } 141 }
29 142
30 // spool directory contains either links to real printer devices or just simple files
31 // these links or files are called "queues"
32 // OR
33 // if a directory named as given queue exists within spool directory
34 // then LPD enters spooling mode and just dumps both control and data files to it
35
36 // goto spool directory 143 // goto spool directory
37 if (argv[1]) 144 if (*++argv)
38 xchdir(argv[1]); 145 xchdir(*argv++);
39 146
40 // parse command: "\x2QUEUE_NAME\n" 147 // parse command: "\x2QUEUE_NAME\n"
41 queue = s + 1; 148 queue = s + 1;
42 *strchrnul(s, '\n') = '\0'; 149 *strchrnul(s, '\n') = '\0';
43 150
44 // protect against "/../" attacks 151 // protect against "/../" attacks
45 if (queue[0] == '.' || strstr(queue, "/.")) 152 if (!*sane(queue))
46 return EXIT_FAILURE; 153 return EXIT_FAILURE;
47 154
48 // queue is a directory -> chdir to it and enter spooling mode 155 // queue is a directory -> chdir to it and enter spooling mode
@@ -80,10 +187,8 @@ int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[])
80 *fname++ = '\0'; 187 *fname++ = '\0';
81 if (spooling) { 188 if (spooling) {
82 // spooling mode: dump both files 189 // spooling mode: dump both files
83 // make "/../" attacks in file names ineffective
84 xchroot(".");
85 // job in flight has mode 0200 "only writable" 190 // job in flight has mode 0200 "only writable"
86 fd = xopen3(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200); 191 fd = xopen3(sane(fname), O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200);
87 } else { 192 } else {
88 // non-spooling mode: 193 // non-spooling mode:
89 // 2: control file (ignoring), 3: data file 194 // 2: control file (ignoring), 3: data file
@@ -99,6 +204,20 @@ int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[])
99 expected_len, real_len); 204 expected_len, real_len);
100 return EXIT_FAILURE; 205 return EXIT_FAILURE;
101 } 206 }
207 // chmod completely downloaded file as "readable+writable" ...
208 if (spooling) {
209 fchmod(fd, 0600);
210 // ... and accumulate dump state.
211 // N.B. after all files are dumped spooling should be 1+2+3==6
212 spooling += s[0];
213 }
214 close(fd); // NB: can do close(-1). Who cares?
215
216 // are all files dumped? -> spawn spool helper
217 if (6 == spooling && *argv) {
218 fname[0] = 'c'; // pass control file name
219 exec_helper(fname, argv);
220 }
102 // get ACK and see whether it is NUL (ok) 221 // get ACK and see whether it is NUL (ok)
103 if (read(STDIN_FILENO, s, 1) != 1 || s[0] != 0) { 222 if (read(STDIN_FILENO, s, 1) != 1 || s[0] != 0) {
104 // don't send error msg to peer - it obviously 223 // don't send error msg to peer - it obviously
@@ -106,10 +225,6 @@ int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[])
106 // it can't understand us either 225 // it can't understand us either
107 return EXIT_FAILURE; 226 return EXIT_FAILURE;
108 } 227 }
109 // chmod completely downloaded job as "readable+writable"
110 if (spooling)
111 fchmod(fd, 0600);
112 close(fd); // NB: can do close(-1). Who cares?
113 free(s); 228 free(s);
114 } /* while (1) */ 229 } /* while (1) */
115} 230}