1 /*        $NetBSD: rmjob.c,v 1.25 2009/07/13 19:05:41 roy Exp $       */
2 
3 /*
4  * Copyright (c) 1983, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)rmjob.c     8.2 (Berkeley) 4/28/95";
36 #else
37 __RCSID("$NetBSD: rmjob.c,v 1.25 2009/07/13 19:05:41 roy Exp $");
38 #endif
39 #endif /* not lint */
40 
41 #include <sys/param.h>
42 
43 #include <signal.h>
44 #include <errno.h>
45 #include <dirent.h>
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <string.h>
50 #include <ctype.h>
51 #include "lp.h"
52 #include "lp.local.h"
53 #include "pathnames.h"
54 
55 /*
56  * rmjob - remove the specified jobs from the queue.
57  */
58 
59 /*
60  * Stuff for handling lprm specifications
61  */
62 extern char         *user[];            /* users to process */
63 extern int          users;                        /* # of users in user array */
64 extern int          requ[];                       /* job number of spool entries */
65 extern int          requests;           /* # of spool requests */
66 extern char         *person;            /* name of person doing lprm */
67 
68 static char         root[] = "root";
69 static int          all = 0;            /* eliminate all files (root only) */
70 static int          cur_daemon;                   /* daemon's pid */
71 static char         current[40];                  /* active control file name */
72 
73 extern uid_t        uid, euid;                    /* real and effective user id's */
74 
75 static    void      do_unlink(const char *);
76 static    void      alarmer(int);
77 
78 void
rmjob(void)79 rmjob(void)
80 {
81           int i, nitems;
82           int assasinated = 0;
83           struct dirent **files;
84 
85           getprintcap(printer);
86 
87           /*
88            * If the format was `lprm -' and the user isn't the super-user,
89            *  then fake things to look like he said `lprm user'.
90            */
91           if (users < 0) {
92                     if (getuid() == 0)
93                               all = 1;  /* all files in local queue */
94                     else {
95                               user[0] = person;
96                               users = 1;
97                     }
98           }
99           if (!strcmp(person, "-all")) {
100                     if (from == host)
101                               fatal("The login name \"-all\" is reserved");
102                     all = 1;  /* all those from 'from' */
103                     person = root;
104           }
105 
106           seteuid(euid);
107           if (chdir(SD) < 0)
108                     fatal("cannot chdir to spool directory");
109           if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
110                     fatal("cannot access spool directory");
111           seteuid(uid);
112 
113           if (nitems) {
114                     /*
115                      * Check for an active printer daemon (in which case we
116                      *  kill it if it is reading our file) then remove stuff
117                      *  (after which we have to restart the daemon).
118                      */
119                     if (lockchk(LO) && chk(current)) {
120                               seteuid(euid);
121                               assasinated = kill(cur_daemon, SIGINT) == 0;
122                               seteuid(uid);
123                               if (!assasinated)
124                                         fatal("cannot kill printer daemon");
125                     }
126                     /*
127                      * process the files
128                      */
129                     for (i = 0; i < nitems; i++)
130                               process(files[i]->d_name);
131           }
132           rmremote();
133           /*
134            * Restart the printer daemon if it was killed
135            */
136           if (assasinated && !startdaemon(printer))
137                     fatal("cannot restart printer daemon\n");
138           exit(0);
139 }
140 
141 /*
142  * Process a lock file: collect the pid of the active
143  *  daemon and the file name of the active spool entry.
144  * Return boolean indicating existence of a lock file.
145  */
146 int
lockchk(const char * s)147 lockchk(const char *s)
148 {
149           FILE *fp;
150           int i, n;
151 
152           seteuid(euid);
153           if ((fp = fopen(s, "r")) == NULL) {
154                     if (errno == EACCES)
155                               fatal("can't access lock file");
156                     else
157                               return(0);
158           }
159           seteuid(uid);
160           if (!get_line(fp)) {
161                     (void)fclose(fp);
162                     return(0);                    /* no daemon present */
163           }
164           cur_daemon = atoi(line);
165           if (kill(cur_daemon, 0) < 0 && errno != EPERM) {
166                     (void)fclose(fp);
167                     return(0);                    /* no daemon present */
168           }
169           for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) {
170                     if (i > 5) {
171                               n = 1;
172                               break;
173                     }
174                     sleep((size_t)i);
175           }
176           current[n-1] = '\0';
177           (void)fclose(fp);
178           return(1);
179 }
180 
181 /*
182  * Process a control file.
183  */
184 void
process(const char * file)185 process(const char *file)
186 {
187           FILE *cfp;
188 
189           if (!chk(file))
190                     return;
191           seteuid(euid);
192           if ((cfp = fopen(file, "r")) == NULL)
193                     fatal("cannot open %s", file);
194           seteuid(uid);
195           while (get_line(cfp)) {
196                     switch (line[0]) {
197                     case 'U':  /* unlink associated files */
198                               if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
199                                         break;
200                               do_unlink(line+1);
201                     }
202           }
203           (void)fclose(cfp);
204           do_unlink(file);
205 }
206 
207 static void
do_unlink(const char * file)208 do_unlink(const char *file)
209 {
210           int       ret;
211 
212           if (from != host)
213                     printf("%s: ", host);
214           seteuid(euid);
215           ret = unlink(file);
216           seteuid(uid);
217           printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
218 }
219 
220 /*
221  * Do the dirty work in checking
222  */
223 int
chk(const char * file)224 chk(const char *file)
225 {
226           int *r, n;
227           char **u;
228           const char *cp;
229           FILE *cfp;
230 
231           /*
232            * Check for valid cf file name (mostly checking current).
233            */
234           if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
235                     return(0);
236 
237           if (all && (from == host || !strcmp(from, file+6)))
238                     return(1);
239 
240           /*
241            * get the owner's name from the control file.
242            */
243           seteuid(euid);
244           if ((cfp = fopen(file, "r")) == NULL)
245                     return(0);
246           seteuid(uid);
247           while (get_line(cfp)) {
248                     if (line[0] == 'P')
249                               break;
250           }
251           (void)fclose(cfp);
252           if (line[0] != 'P')
253                     return(0);
254 
255           if (users == 0 && requests == 0)
256                     return(!strcmp(file, current) && isowner(line+1, file));
257           /*
258            * Check the request list
259            */
260           for (n = 0, cp = file+3; isdigit((unsigned char)*cp); )
261                     n = n * 10 + (*cp++ - '0');
262           for (r = requ; r < &requ[requests]; r++)
263                     if (*r == n && isowner(line+1, file))
264                               return(1);
265           /*
266            * Check to see if it's in the user list
267            */
268           for (u = user; u < &user[users]; u++)
269                     if (!strcmp(*u, line+1) && isowner(line+1, file))
270                               return(1);
271           return(0);
272 }
273 
274 /*
275  * If root is removing a file on the local machine, allow it.
276  * If root is removing a file from a remote machine, only allow
277  * files sent from the remote machine to be removed.
278  * Normal users can only remove the file from where it was sent.
279  */
280 int
isowner(const char * owner,const char * file)281 isowner(const char *owner, const char *file)
282 {
283           if (!strcmp(person, root) && (from == host || !strcmp(from, file+6)))
284                     return(1);
285           if (!strcmp(person, owner) && !strcmp(from, file+6))
286                     return(1);
287           if (from != host)
288                     printf("%s: ", host);
289           printf("%s: Permission denied\n", file);
290           return(0);
291 }
292 
293 /*
294  * Check to see if we are sending files to a remote machine. If we are,
295  * then try removing files on the remote machine.
296  */
297 void
rmremote(void)298 rmremote(void)
299 {
300           char *cp, *s;
301           int i, rem;
302           size_t len;
303 
304           if (!remote)
305                     return;   /* not sending to a remote machine */
306 
307           /*
308            * Flush stdout so the user can see what has been deleted
309            * while we wait (possibly) for the connection.
310            */
311           fflush(stdout);
312 
313           /* \5 RP space all */
314           len = 1 + strlen(RP) + 1 + strlen(all ? "-all" : person);
315           for (i = 0; i < users; i++) {
316                     len += strlen(user[i]) + 1;
317           }
318           for (i = 0; i < requests; i++) {
319                     len += snprintf(line, sizeof(line), " %d", requ[i]);
320           }
321           /* newline nul */
322           len += 2;
323           if (len > sizeof(line))
324                     s = malloc(len);
325           else
326                     s = line;
327           cp = s;
328 
329           cp += snprintf(s, len, "\5%s %s", RP, all ? "-all" : person);
330           for (i = 0; i < users; i++) {
331                     *cp++ = ' ';
332                     strncpy(cp, user[i], len - (cp - s) - 2);
333                     cp += strlen(cp);
334           }
335           for (i = 0; i < requests; i++) {
336                     (void)snprintf(cp, len - (cp - s) - 1, " %d", requ[i]);
337                     cp += strlen(cp);
338           }
339           cp[0] = '\n';
340           cp[1] = '\0';
341 
342           rem = getport(RM);
343           if (rem < 0) {
344                     if (from != host)
345                               printf("%s: ", host);
346                     printf("connection to %s is down\n", RM);
347           } else {
348                     struct sigaction osa, nsa;
349 
350                     if ((size_t)write(rem, s, len) != len)
351                               fatal("Lost connection");
352                     if (len > sizeof(line))
353                               (void)free(s);
354                     nsa.sa_handler = alarmer;
355                     sigemptyset(&nsa.sa_mask);
356                     sigaddset(&nsa.sa_mask, SIGALRM);
357                     nsa.sa_flags = 0;
358                     (void)sigaction(SIGALRM, &nsa, &osa);
359                     alarm(wait_time);
360                     while ((i = read(rem, line, sizeof(line))) > 0) {
361                               (void)fwrite(line, 1, (size_t)i, stdout);
362                               alarm(wait_time);
363                     }
364                     alarm(0);
365                     (void)sigaction(SIGALRM, &osa, NULL);
366                     (void)close(rem);
367           }
368 }
369 
370 static void
alarmer(int s)371 alarmer(int s)
372 {
373           /* nothing */
374 }
375 
376 /*
377  * Return 1 if the filename begins with 'cf'
378  */
379 int
iscf(const struct dirent * d)380 iscf(const struct dirent *d)
381 {
382           return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
383 }
384