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