diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-23 23:40:18 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-23 23:40:18 +0000 |
commit | a79428998d76c1758ca12546e5db945a0cd64518 (patch) | |
tree | 88a26fd659f414fbdfeb9208be708d7339a2bf02 | |
parent | c93b162248892265eae5f54e9ee409074dfa08c5 (diff) | |
download | busybox-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.h | 4 | ||||
-rw-r--r-- | printutils/lpd.c | 147 |
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 | ||
64 | static 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 | |||
78 | static 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 | |||
13 | int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; | 126 | int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE; |
14 | int lpd_main(int argc ATTRIBUTE_UNUSED, char *argv[]) | 127 | int 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 | } |