1/* 2 * scannedonly VFS module for Samba 3.5 3 * 4 * Copyright 2007,2008,2009 (C) Olivier Sessink 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 * 20 * ABOUT SCANNEDONLY 21 * 22 * scannedonly implements a 'filter' like vfs module that talks over a 23 * unix domain socket or over UDP to a anti-virus engine. 24 * 25 * files that are clean have a corresponding .scanned:{filename} file 26 * in the same directory. So why the .scanned: files? They take up 27 * only an inode, because they are 0 bytes. To test if the file is 28 * scanned only a stat() call on the filesystem is needed which is 29 * very quick compared to a database lookup. All modern filesystems 30 * use database technology such as balanced trees for lookups anyway. 31 * The number of inodes in modern filesystems is also not limiting 32 * anymore. The .scanned: files are also easy scriptable. You can 33 * remove them with a simple find command or create them with a 34 * simple touch command. Extended filesystem attributes have similar 35 * properties, but are not supported on all filesystems, so that 36 * would limit the usage of the module (and attributes are not as 37 * easily scriptable) 38 * 39 * files that are not clean are sent to the AV-engine. Only the 40 * filename is sent over the socket. The protocol is very simple: 41 * a newline separated list of filenames inside each datagram. 42 * 43 * a file AV-scan may be requested multiple times, the AV-engine 44 * should also check if the file has been scanned already. Requests 45 * can also be dropped by the AV-engine (and we thus don't need the 46 * reliability of TCP). 47 * 48 */ 49 50#include "includes.h" 51 52#include "config.h" 53 54#define SENDBUFFERSIZE 1450 55 56#ifndef SUN_LEN 57#define SUN_LEN(sunp) ((size_t)((struct sockaddr_un *)0)->sun_path \ 58 + strlen((sunp)->sun_path)) 59#endif 60 61 62struct Tscannedonly { 63 int socket; 64 int domain_socket; 65 int portnum; 66 int scanning_message_len; 67 int recheck_time_open; 68 int recheck_tries_open; 69 int recheck_size_open; 70 int recheck_time_readdir; 71 int recheck_tries_readdir; 72 bool show_special_files; 73 bool rm_hidden_files_on_rmdir; 74 bool hide_nonscanned_files; 75 bool allow_nonscanned_files; 76 char *socketname; 77 char *scanhost; 78 char *scanning_message; 79 char *p_scanned; /* prefix for scanned files */ 80 char *p_virus; /* prefix for virus containing files */ 81 char *p_failed; /* prefix for failed to scan files */ 82 char gsendbuffer[SENDBUFFERSIZE + 1]; 83}; 84 85#define STRUCTSCANO(var) ((struct Tscannedonly *)var) 86 87struct scannedonly_DIR { 88 char *base; 89 int notify_loop_done; 90 SMB_STRUCT_DIR *DIR; 91}; 92#define SCANNEDONLY_DEBUG 9 93/*********************/ 94/* utility functions */ 95/*********************/ 96 97static char *real_path_from_notify_path(TALLOC_CTX *ctx, 98 struct Tscannedonly *so, 99 const char *path) 100{ 101 char *name; 102 int len, pathlen; 103 104 name = strrchr(path, '/'); 105 if (!name) { 106 return NULL; 107 } 108 pathlen = name - path; 109 name++; 110 len = strlen(name); 111 if (len <= so->scanning_message_len) { 112 return NULL; 113 } 114 115 if (strcmp(name + (len - so->scanning_message_len), 116 so->scanning_message) != 0) { 117 return NULL; 118 } 119 120 return talloc_strndup(ctx,path, 121 pathlen + len - so->scanning_message_len); 122} 123 124static char *cachefile_name(TALLOC_CTX *ctx, 125 const char *shortname, 126 const char *base, 127 const char *p_scanned) 128{ 129 return talloc_asprintf(ctx, "%s%s%s", base, p_scanned, shortname); 130} 131 132static char *name_w_ending_slash(TALLOC_CTX *ctx, const char *name) 133{ 134 int len = strlen(name); 135 if (name[len - 1] == '/') { 136 return talloc_strdup(ctx,name); 137 } else { 138 return talloc_asprintf(ctx, "%s/", name); 139 } 140} 141 142static char *cachefile_name_f_fullpath(TALLOC_CTX *ctx, 143 const char *fullpath, 144 const char *p_scanned) 145{ 146 const char *base; 147 char *tmp, *cachefile, *shortname; 148 tmp = strrchr(fullpath, '/'); 149 if (tmp) { 150 base = talloc_strndup(ctx, fullpath, (tmp - fullpath) + 1); 151 shortname = tmp + 1; 152 } else { 153 base = ""; 154 shortname = (char *)fullpath; 155 } 156 cachefile = cachefile_name(ctx, shortname, base, p_scanned); 157 DEBUG(SCANNEDONLY_DEBUG, 158 ("cachefile_name_f_fullpath cachefile=%s\n", cachefile)); 159 return cachefile; 160} 161 162static char *construct_full_path(TALLOC_CTX *ctx, vfs_handle_struct * handle, 163 const char *somepath, bool ending_slash) 164{ 165 char *tmp; 166 167 if (!somepath) { 168 return NULL; 169 } 170 if (somepath[0] == '/') { 171 if (ending_slash) { 172 return name_w_ending_slash(ctx,somepath); 173 } 174 return talloc_strdup(ctx,somepath); 175 } 176 tmp=(char *)somepath; 177 if (tmp[0]=='.'&&tmp[1]=='/') { 178 tmp+=2; 179 } 180 /* vfs_GetWd() seems to return a path with a slash */ 181 if (ending_slash) { 182 return talloc_asprintf(ctx, "%s/%s/", 183 vfs_GetWd(ctx, handle->conn),tmp); 184 } 185 return talloc_asprintf(ctx, "%s/%s", 186 vfs_GetWd(ctx, handle->conn),tmp); 187} 188 189static int connect_to_scanner(vfs_handle_struct * handle) 190{ 191 struct Tscannedonly *so = (struct Tscannedonly *)handle->data; 192 193 if (so->domain_socket) { 194 struct sockaddr_un saun; 195 DEBUG(SCANNEDONLY_DEBUG, ("socket=%s\n", so->socketname)); 196 if ((so->socket = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) { 197 DEBUG(2, ("failed to create socket %s\n", 198 so->socketname)); 199 return -1; 200 } 201 saun.sun_family = AF_UNIX; 202 strncpy(saun.sun_path, so->socketname, 203 sizeof(saun.sun_path) - 1); 204 if (connect(so->socket, (struct sockaddr *)(void *)&saun, 205 SUN_LEN(&saun)) < 0) { 206 DEBUG(2, ("failed to connect to socket %s\n", 207 so->socketname)); 208 return -1; 209 } 210 DEBUG(SCANNEDONLY_DEBUG,("bound %s to socket %d\n", 211 saun.sun_path, so->socket)); 212 213 } else { 214 so->socket = open_udp_socket(so->scanhost, so->portnum); 215 if (so->socket < 0) { 216 DEBUG(2,("failed to open UDP socket to %s:%d\n", 217 so->scanhost,so->portnum)); 218 return -1; 219 } 220 } 221 222 {/* increasing the socket buffer is done because we have large bursts 223 of UDP packets or DGRAM's on a domain socket whenever we hit a 224 large directory with lots of unscanned files. */ 225 int sndsize; 226 socklen_t size = sizeof(int); 227 getsockopt(so->socket, SOL_SOCKET, SO_RCVBUF, 228 (char *)&sndsize, &size); 229 DEBUG(SCANNEDONLY_DEBUG, ("current socket buffer size=%d\n", 230 sndsize)); 231 sndsize = 262144; 232 if (setsockopt(so->socket, SOL_SOCKET, SO_RCVBUF, 233 (char *)&sndsize, 234 (int)sizeof(sndsize)) != 0) { 235 DEBUG(SCANNEDONLY_DEBUG, 236 ("error setting socket buffer %s (%d)\n", 237 strerror(errno), errno)); 238 } 239 } 240 set_blocking(so->socket, false); 241 return 0; 242} 243 244static void flush_sendbuffer(vfs_handle_struct * handle) 245{ 246 struct Tscannedonly *so = (struct Tscannedonly *)handle->data; 247 int ret, len, loop = 10; 248 if (so->gsendbuffer[0] == '\0') { 249 return; 250 } 251 252 do { 253 loop--; 254 len = strlen(so->gsendbuffer); 255 ret = send(so->socket, so->gsendbuffer, len, 0); 256 if (ret == len) { 257 so->gsendbuffer[0] = '\0'; 258 break; 259 } 260 if (ret == -1) { 261 DEBUG(3,("scannedonly flush_sendbuffer: " 262 "error sending on socket %d to scanner:" 263 " %s (%d)\n", 264 so->socket, strerror(errno), errno)); 265 if (errno == ECONNREFUSED || errno == ENOTCONN 266 || errno == ECONNRESET) { 267 if (connect_to_scanner(handle) == -1) 268 break; /* connecting fails, abort */ 269 /* try again */ 270 } else if (errno != EINTR) { 271 /* on EINTR we just try again, all remaining 272 other errors we log the error 273 and try again ONCE */ 274 loop = 1; 275 DEBUG(3,("scannedonly flush_sendbuffer: " 276 "error sending data to scanner: %s " 277 "(%d)\n", strerror(errno), errno)); 278 } 279 } else { 280 /* --> partial write: Resend all filenames that were 281 not or not completely written. a partial filename 282 written means the filename will not arrive correctly, 283 so resend it completely */ 284 int pos = 0; 285 while (pos < len) { 286 char *tmp = strchr(so->gsendbuffer+pos, '\n'); 287 if (tmp && tmp - so->gsendbuffer < ret) 288 pos = tmp - so->gsendbuffer + 1; 289 else 290 break; 291 } 292 memmove(so->gsendbuffer, so->gsendbuffer + pos, 293 SENDBUFFERSIZE - ret); 294 /* now try again */ 295 } 296 } while (loop > 0); 297 298 if (so->gsendbuffer[0] != '\0') { 299 DEBUG(2, 300 ("scannedonly flush_sendbuffer: " 301 "failed to send files to AV scanner, " 302 "discarding files.")); 303 so->gsendbuffer[0] = '\0'; 304 } 305} 306 307static void notify_scanner(vfs_handle_struct * handle, const char *scanfile) 308{ 309 char *tmp; 310 int tmplen, gsendlen; 311 struct Tscannedonly *so = (struct Tscannedonly *)handle->data; 312 TALLOC_CTX *ctx=talloc_tos(); 313 if (scanfile[0] != '/') { 314 tmp = construct_full_path(ctx,handle, scanfile, false); 315 } else { 316 tmp = (char *)scanfile; 317 } 318 tmplen = strlen(tmp); 319 gsendlen = strlen(so->gsendbuffer); 320 DEBUG(SCANNEDONLY_DEBUG, 321 ("scannedonly notify_scanner: tmp=%s, tmplen=%d, gsendlen=%d\n", 322 tmp, tmplen, gsendlen)); 323 if (gsendlen + tmplen >= SENDBUFFERSIZE) { 324 flush_sendbuffer(handle); 325 } 326 strlcat(so->gsendbuffer, tmp, SENDBUFFERSIZE + 1); 327 strlcat(so->gsendbuffer, "\n", SENDBUFFERSIZE + 1); 328} 329 330static bool is_scannedonly_file(struct Tscannedonly *so, const char *shortname) 331{ 332 if (shortname[0]!='.') { 333 return false; 334 } 335 if (strncmp(shortname, so->p_scanned, strlen(so->p_scanned)) == 0) { 336 return true; 337 } 338 if (strncmp(shortname, so->p_virus, strlen(so->p_virus)) == 0) { 339 return true; 340 } 341 if (strncmp(shortname, so->p_failed, strlen(so->p_failed)) == 0) { 342 return true; 343 } 344 return false; 345} 346 347static bool timespec_is_newer(struct timespec *base, struct timespec *test) 348{ 349 return timespec_compare(base,test) < 0; 350} 351 352/* 353vfs_handle_struct *handle the scannedonly handle 354scannedonly_DIR * sDIR the scannedonly struct if called from _readdir() 355or NULL 356fullpath is a full path starting from / or a relative path to the 357current working directory 358shortname is the filename without directory components 359basename, is the directory without file name component 360allow_nonexistant return TRUE if stat() on the requested file fails 361recheck_time, the time in milliseconds to wait for the daemon to 362create a .scanned file 363recheck_tries, the number of tries to wait 364recheck_size, size in Kb of files that should not be waited for 365loop : boolean if we should try to loop over all files in the directory 366and send a notify to the scanner for all files that need scanning 367*/ 368static bool scannedonly_allow_access(vfs_handle_struct * handle, 369 struct scannedonly_DIR *sDIR, 370 struct smb_filename *smb_fname, 371 const char *shortname, 372 const char *base_name, 373 int allow_nonexistant, 374 int recheck_time, int recheck_tries, 375 int recheck_size, int loop) 376{ 377 struct smb_filename *cache_smb_fname; 378 TALLOC_CTX *ctx=talloc_tos(); 379 char *cachefile; 380 int retval; 381 int didloop; 382 DEBUG(SCANNEDONLY_DEBUG, 383 ("smb_fname->base_name=%s, shortname=%s, base_name=%s\n" 384 ,smb_fname->base_name,shortname,base_name)); 385 386 if (ISDOT(shortname) || ISDOTDOT(shortname)) { 387 return true; 388 } 389 if (is_scannedonly_file(STRUCTSCANO(handle->data), shortname)) { 390 DEBUG(SCANNEDONLY_DEBUG, 391 ("scannedonly_allow_access, %s is a scannedonly file, " 392 "return 0\n", shortname)); 393 return false; 394 } 395 396 if (!VALID_STAT(smb_fname->st)) { 397 DEBUG(SCANNEDONLY_DEBUG,("stat %s\n",smb_fname->base_name)); 398 retval = SMB_VFS_NEXT_STAT(handle, smb_fname); 399 if (retval != 0) { 400 /* failed to stat this file?!? --> hide it */ 401 DEBUG(SCANNEDONLY_DEBUG,("no valid stat, return" 402 " allow_nonexistant=%d\n", 403 allow_nonexistant)); 404 return allow_nonexistant; 405 } 406 } 407 if (!S_ISREG(smb_fname->st.st_ex_mode)) { 408 DEBUG(SCANNEDONLY_DEBUG, 409 ("%s is not a regular file, ISDIR=%d\n", 410 smb_fname->base_name, 411 S_ISDIR(smb_fname->st.st_ex_mode))); 412 return (STRUCTSCANO(handle->data)-> 413 show_special_files || 414 S_ISDIR(smb_fname->st.st_ex_mode)); 415 } 416 if (smb_fname->st.st_ex_size == 0) { 417 DEBUG(SCANNEDONLY_DEBUG,("empty file, return 1\n")); 418 return true; /* empty files cannot contain viruses ! */ 419 } 420 cachefile = cachefile_name(ctx, 421 shortname, 422 base_name, 423 STRUCTSCANO(handle->data)->p_scanned); 424 create_synthetic_smb_fname(ctx, cachefile,NULL,NULL,&cache_smb_fname); 425 if (!VALID_STAT(cache_smb_fname->st)) { 426 retval = SMB_VFS_NEXT_STAT(handle, cache_smb_fname); 427 } 428 if (retval == 0 && VALID_STAT(cache_smb_fname->st)) { 429 if (timespec_is_newer(&smb_fname->st.st_ex_ctime, 430 &cache_smb_fname->st.st_ex_ctime)) { 431 talloc_free(cache_smb_fname); 432 return true; 433 } 434 /* no cachefile or too old */ 435 SMB_VFS_NEXT_UNLINK(handle, cache_smb_fname); 436 retval = -1; 437 } 438 439 notify_scanner(handle, smb_fname->base_name); 440 441 didloop = 0; 442 if (loop && sDIR && !sDIR->notify_loop_done) { 443 /* check the rest of the directory and notify the 444 scanner if some file needs scanning */ 445 long offset; 446 SMB_STRUCT_DIRENT *dire; 447 448 offset = SMB_VFS_NEXT_TELLDIR(handle, sDIR->DIR); 449 dire = SMB_VFS_NEXT_READDIR(handle, sDIR->DIR, NULL); 450 while (dire) { 451 char *fpath2; 452 struct smb_filename *smb_fname2; 453 fpath2 = talloc_asprintf(ctx, "%s%s", base_name,dire->d_name); 454 DEBUG(SCANNEDONLY_DEBUG, 455 ("scannedonly_allow_access in loop, " 456 "found %s\n", fpath2)); 457 create_synthetic_smb_fname(ctx, fpath2,NULL,NULL, 458 &smb_fname2); 459 scannedonly_allow_access(handle, NULL, 460 smb_fname2, 461 dire->d_name, 462 base_name, 0, 0, 0, 0, 0); 463 talloc_free(fpath2); 464 talloc_free(smb_fname2); 465 dire = SMB_VFS_NEXT_READDIR(handle, sDIR->DIR,NULL); 466 } 467 sDIR->notify_loop_done = 1; 468 didloop = 1; 469 SMB_VFS_NEXT_SEEKDIR(handle, sDIR->DIR, offset); 470 } 471 if (recheck_time > 0 472 && ((recheck_size > 0 473 && smb_fname->st.st_ex_size < (1024 * recheck_size)) 474 || didloop)) { 475 int i = 0; 476 flush_sendbuffer(handle); 477 while (retval != 0 /*&& errno == ENOENT */ 478 && i < recheck_tries) { 479 struct timespec req = { 0, recheck_time * 10000 }; 480 DEBUG(SCANNEDONLY_DEBUG, 481 ("scannedonly_allow_access, wait (try=%d " 482 "(max %d), %d ms) for %s\n", 483 i, recheck_tries, 484 recheck_time, cache_smb_fname->base_name)); 485 nanosleep(&req, NULL); 486 retval = SMB_VFS_NEXT_STAT(handle, cache_smb_fname); 487 i++; 488 } 489 } 490 /* still no cachefile, or still too old, return 0 */ 491 if (retval != 0 492 || !timespec_is_newer(&smb_fname->st.st_ex_ctime, 493 &cache_smb_fname->st.st_ex_ctime)) { 494 DEBUG(SCANNEDONLY_DEBUG, 495 ("retval=%d, return 0\n",retval)); 496 return false; 497 } 498 return true; 499} 500 501/*********************/ 502/* VFS functions */ 503/*********************/ 504 505static SMB_STRUCT_DIR *scannedonly_opendir(vfs_handle_struct * handle, 506 const char *fname, 507 const char *mask, uint32 attr) 508{ 509 SMB_STRUCT_DIR *DIRp; 510 struct scannedonly_DIR *sDIR; 511 512 DIRp = SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr); 513 if (!DIRp) { 514 return NULL; 515 } 516 517 sDIR = TALLOC_P(NULL, struct scannedonly_DIR); 518 if (fname[0] != '/') { 519 sDIR->base = construct_full_path(sDIR,handle, fname, true); 520 } else { 521 sDIR->base = name_w_ending_slash(sDIR, fname); 522 } 523 DEBUG(SCANNEDONLY_DEBUG, 524 ("scannedonly_opendir, fname=%s, base=%s\n",fname,sDIR->base)); 525 sDIR->DIR = DIRp; 526 sDIR->notify_loop_done = 0; 527 return (SMB_STRUCT_DIR *) sDIR; 528} 529 530static SMB_STRUCT_DIRENT *scannedonly_readdir(vfs_handle_struct *handle, 531 SMB_STRUCT_DIR * dirp, 532 SMB_STRUCT_STAT *sbuf) 533{ 534 SMB_STRUCT_DIRENT *result; 535 int allowed = 0; 536 char *tmp; 537 struct smb_filename *smb_fname; 538 char *notify_name; 539 int namelen; 540 SMB_STRUCT_DIRENT *newdirent; 541 TALLOC_CTX *ctx=talloc_tos(); 542 543 struct scannedonly_DIR *sDIR = (struct scannedonly_DIR *)dirp; 544 if (!dirp) { 545 return NULL; 546 } 547 548 result = SMB_VFS_NEXT_READDIR(handle, sDIR->DIR, sbuf); 549 550 if (!result) 551 return NULL; 552 553 if (is_scannedonly_file(STRUCTSCANO(handle->data), result->d_name)) { 554 DEBUG(SCANNEDONLY_DEBUG, 555 ("scannedonly_readdir, %s is a scannedonly file, " 556 "skip to next entry\n", result->d_name)); 557 return scannedonly_readdir(handle, dirp, NULL); 558 } 559 tmp = talloc_asprintf(ctx, "%s%s", sDIR->base, result->d_name); 560 DEBUG(SCANNEDONLY_DEBUG, 561 ("scannedonly_readdir, check access to %s (sbuf=%p)\n", 562 tmp,sbuf)); 563 564 /* even if we don't hide nonscanned files or we allow non scanned 565 files we call allow_access because it will notify the daemon to 566 scan these files */ 567 create_synthetic_smb_fname(ctx, tmp,NULL, 568 sbuf?VALID_STAT(*sbuf)?sbuf:NULL:NULL, 569 &smb_fname); 570 allowed = scannedonly_allow_access( 571 handle, sDIR, smb_fname, 572 result->d_name, 573 sDIR->base, 0, 574 STRUCTSCANO(handle->data)->hide_nonscanned_files 575 ? STRUCTSCANO(handle->data)->recheck_time_readdir 576 : 0, 577 STRUCTSCANO(handle->data)->recheck_tries_readdir, 578 -1, 579 1); 580 DEBUG(SCANNEDONLY_DEBUG, 581 ("scannedonly_readdir access to %s (%s) = %d\n", tmp, 582 result->d_name, allowed)); 583 if (allowed) { 584 return result; 585 } 586 DEBUG(SCANNEDONLY_DEBUG, 587 ("hide_nonscanned_files=%d, allow_nonscanned_files=%d\n", 588 STRUCTSCANO(handle->data)->hide_nonscanned_files, 589 STRUCTSCANO(handle->data)->allow_nonscanned_files 590 )); 591 592 if (!STRUCTSCANO(handle->data)->hide_nonscanned_files 593 || STRUCTSCANO(handle->data)->allow_nonscanned_files) { 594 return result; 595 } 596 597 DEBUG(SCANNEDONLY_DEBUG, 598 ("scannedonly_readdir, readdir listing for %s not " 599 "allowed, notify user\n", result->d_name)); 600 notify_name = talloc_asprintf( 601 ctx,"%s %s",result->d_name, 602 STRUCTSCANO(handle->data)->scanning_message); 603 namelen = strlen(notify_name); 604 newdirent = (SMB_STRUCT_DIRENT *)TALLOC_ARRAY( 605 ctx, char, sizeof(SMB_STRUCT_DIRENT) + namelen + 1); 606 if (!newdirent) { 607 return NULL; 608 } 609 memcpy(newdirent, result, sizeof(SMB_STRUCT_DIRENT)); 610 memcpy(&newdirent->d_name, notify_name, namelen + 1); 611 DEBUG(SCANNEDONLY_DEBUG, 612 ("scannedonly_readdir, return newdirent at %p with " 613 "notification %s\n", newdirent, newdirent->d_name)); 614 return newdirent; 615} 616 617static void scannedonly_seekdir(struct vfs_handle_struct *handle, 618 SMB_STRUCT_DIR * dirp, long offset) 619{ 620 struct scannedonly_DIR *sDIR = (struct scannedonly_DIR *)dirp; 621 SMB_VFS_NEXT_SEEKDIR(handle, sDIR->DIR, offset); 622} 623 624static long scannedonly_telldir(struct vfs_handle_struct *handle, 625 SMB_STRUCT_DIR * dirp) 626{ 627 struct scannedonly_DIR *sDIR = (struct scannedonly_DIR *)dirp; 628 return SMB_VFS_NEXT_TELLDIR(handle, sDIR->DIR); 629} 630 631static void scannedonly_rewinddir(struct vfs_handle_struct *handle, 632 SMB_STRUCT_DIR * dirp) 633{ 634 struct scannedonly_DIR *sDIR = (struct scannedonly_DIR *)dirp; 635 SMB_VFS_NEXT_REWINDDIR(handle, sDIR->DIR); 636} 637 638static int scannedonly_closedir(vfs_handle_struct * handle, 639 SMB_STRUCT_DIR * dirp) 640{ 641 int retval; 642 struct scannedonly_DIR *sDIR = (struct scannedonly_DIR *)dirp; 643 flush_sendbuffer(handle); 644 retval = SMB_VFS_NEXT_CLOSEDIR(handle, sDIR->DIR); 645 TALLOC_FREE(sDIR); 646 return retval; 647} 648 649static int scannedonly_stat(vfs_handle_struct * handle, 650 struct smb_filename *smb_fname) 651{ 652 int ret; 653 ret = SMB_VFS_NEXT_STAT(handle, smb_fname); 654 DEBUG(SCANNEDONLY_DEBUG, ("scannedonly_stat: %s returned %d\n", 655 smb_fname->base_name, ret)); 656 if (ret != 0 && errno == ENOENT) { 657 TALLOC_CTX *ctx=talloc_tos(); 658 char *test_base_name, *tmp_base_name = smb_fname->base_name; 659 /* possibly this was a fake name (file is being scanned for 660 viruses.txt): check for that and create the real name and 661 stat the real name */ 662 test_base_name = real_path_from_notify_path( 663 ctx, 664 STRUCTSCANO(handle->data), 665 smb_fname->base_name); 666 if (test_base_name) { 667 smb_fname->base_name = test_base_name; 668 ret = SMB_VFS_NEXT_STAT(handle, smb_fname); 669 DEBUG(5, ("_stat: %s returned %d\n", 670 test_base_name, ret)); 671 smb_fname->base_name = tmp_base_name; 672 } 673 } 674 return ret; 675} 676 677static int scannedonly_lstat(vfs_handle_struct * handle, 678 struct smb_filename *smb_fname) 679{ 680 int ret; 681 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname); 682 DEBUG(SCANNEDONLY_DEBUG, ("scannedonly_lstat: %s returned %d\n", 683 smb_fname->base_name, ret)); 684 if (ret != 0 && errno == ENOENT) { 685 TALLOC_CTX *ctx=talloc_tos(); 686 char *test_base_name, *tmp_base_name = smb_fname->base_name; 687 /* possibly this was a fake name (file is being scanned for 688 viruses.txt): check for that and create the real name and 689 stat the real name */ 690 test_base_name = real_path_from_notify_path( 691 ctx, STRUCTSCANO(handle->data), smb_fname->base_name); 692 if (test_base_name) { 693 smb_fname->base_name = test_base_name; 694 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname); 695 DEBUG(5, ("_lstat: %s returned %d\n", 696 test_base_name, ret)); 697 smb_fname->base_name = tmp_base_name; 698 } 699 } 700 return ret; 701} 702 703static int scannedonly_open(vfs_handle_struct * handle, 704 struct smb_filename *smb_fname, 705 files_struct * fsp, int flags, mode_t mode) 706{ 707 const char *base; 708 char *tmp, *shortname; 709 int allowed, write_access = 0; 710 TALLOC_CTX *ctx=talloc_tos(); 711 /* if open for writing ignore it */ 712 if ((flags & O_ACCMODE) == O_WRONLY) { 713 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); 714 } 715 if ((flags & O_ACCMODE) == O_RDWR) { 716 write_access = 1; 717 } 718 /* check if this file is scanned already */ 719 tmp = strrchr(smb_fname->base_name, '/'); 720 if (tmp) { 721 base = talloc_strndup(ctx,smb_fname->base_name, 722 (tmp - smb_fname->base_name) + 1); 723 shortname = tmp + 1; 724 } else { 725 base = ""; 726 shortname = (char *)smb_fname->base_name; 727 } 728 allowed = scannedonly_allow_access( 729 handle, NULL, smb_fname, shortname, 730 base, 731 write_access, 732 STRUCTSCANO(handle->data)->recheck_time_open, 733 STRUCTSCANO(handle->data)->recheck_tries_open, 734 STRUCTSCANO(handle->data)->recheck_size_open, 735 0); 736 flush_sendbuffer(handle); 737 DEBUG(SCANNEDONLY_DEBUG, ("scannedonly_open: allow=%d for %s\n", 738 allowed, smb_fname->base_name)); 739 if (allowed 740 || STRUCTSCANO(handle->data)->allow_nonscanned_files) { 741 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode); 742 } 743 errno = EACCES; 744 return -1; 745} 746 747static int scannedonly_close(vfs_handle_struct * handle, files_struct * fsp) 748{ 749 /* we only have to notify the scanner 750 for files that were open readwrite or writable. */ 751 if (fsp->can_write) { 752 TALLOC_CTX *ctx = talloc_tos(); 753 notify_scanner(handle, construct_full_path( 754 ctx,handle, 755 fsp->fsp_name->base_name,false)); 756 flush_sendbuffer(handle); 757 } 758 return SMB_VFS_NEXT_CLOSE(handle, fsp); 759} 760 761static int scannedonly_rename(vfs_handle_struct * handle, 762 const struct smb_filename *smb_fname_src, 763 const struct smb_filename *smb_fname_dst) 764{ 765 /* rename the cache file before we pass the actual rename on */ 766 struct smb_filename *smb_fname_src_tmp = NULL; 767 struct smb_filename *smb_fname_dst_tmp = NULL; 768 char *cachefile_src, *cachefile_dst; 769 TALLOC_CTX *ctx = talloc_tos(); 770 771 /* Setup temporary smb_filename structs. */ 772 cachefile_src = cachefile_name_f_fullpath( 773 ctx, 774 smb_fname_src->base_name, 775 STRUCTSCANO(handle->data)->p_scanned); 776 cachefile_dst = cachefile_name_f_fullpath( 777 ctx, 778 smb_fname_dst->base_name, 779 STRUCTSCANO(handle->data)->p_scanned); 780 781 create_synthetic_smb_fname(ctx, cachefile_src,NULL,NULL, 782 &smb_fname_src_tmp); 783 create_synthetic_smb_fname(ctx, cachefile_dst,NULL,NULL, 784 &smb_fname_dst_tmp); 785 786 if (SMB_VFS_NEXT_RENAME(handle, smb_fname_src_tmp, smb_fname_dst_tmp) 787 != 0) { 788 DEBUG(SCANNEDONLY_DEBUG, 789 ("failed to rename %s into %s\n", cachefile_src, 790 cachefile_dst)); 791 } 792 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst); 793} 794 795static int scannedonly_unlink(vfs_handle_struct * handle, 796 const struct smb_filename *smb_fname) 797{ 798 /* unlink the 'scanned' file too */ 799 struct smb_filename *smb_fname_cache = NULL; 800 char * cachefile; 801 TALLOC_CTX *ctx = talloc_tos(); 802 803 cachefile = cachefile_name_f_fullpath( 804 ctx, 805 smb_fname->base_name, 806 STRUCTSCANO(handle->data)->p_scanned); 807 create_synthetic_smb_fname(ctx, cachefile,NULL,NULL, 808 &smb_fname_cache); 809 if (SMB_VFS_NEXT_UNLINK(handle, smb_fname_cache) != 0) { 810 DEBUG(SCANNEDONLY_DEBUG, ("_unlink: failed to unlink %s\n", 811 smb_fname_cache->base_name)); 812 } 813 return SMB_VFS_NEXT_UNLINK(handle, smb_fname); 814} 815 816static int scannedonly_rmdir(vfs_handle_struct * handle, const char *path) 817{ 818 /* if there are only .scanned: .virus: or .failed: files, we delete 819 those, because the client cannot see them */ 820 DIR *dirp; 821 SMB_STRUCT_DIRENT *dire; 822 TALLOC_CTX *ctx = talloc_tos(); 823 bool only_deletable_files = true, have_files = false; 824 char *path_w_slash; 825 826 if (!STRUCTSCANO(handle->data)->rm_hidden_files_on_rmdir) 827 return SMB_VFS_NEXT_RMDIR(handle, path); 828 829 path_w_slash = name_w_ending_slash(ctx,path); 830 dirp = SMB_VFS_NEXT_OPENDIR(handle, path, NULL, 0); 831 if (!dirp) { 832 return -1; 833 } 834 while ((dire = SMB_VFS_NEXT_READDIR(handle, dirp, NULL)) != NULL) { 835 if (ISDOT(dire->d_name) || ISDOTDOT(dire->d_name)) { 836 continue; 837 } 838 have_files = true; 839 if (!is_scannedonly_file(STRUCTSCANO(handle->data), 840 dire->d_name)) { 841 struct smb_filename *smb_fname = NULL; 842 char *fullpath; 843 int retval; 844 845 if (STRUCTSCANO(handle->data)->show_special_files) { 846 only_deletable_files = false; 847 break; 848 } 849 /* stat the file and see if it is a 850 special file */ 851 fullpath = talloc_asprintf(ctx, "%s%s", path_w_slash, 852 dire->d_name); 853 create_synthetic_smb_fname(ctx, fullpath,NULL,NULL, 854 &smb_fname); 855 retval = SMB_VFS_NEXT_STAT(handle, smb_fname); 856 if (retval == 0 857 && S_ISREG(smb_fname->st.st_ex_mode)) { 858 only_deletable_files = false; 859 } 860 TALLOC_FREE(fullpath); 861 TALLOC_FREE(smb_fname); 862 break; 863 } 864 } 865 DEBUG(SCANNEDONLY_DEBUG, 866 ("path=%s, have_files=%d, only_deletable_files=%d\n", 867 path, have_files, only_deletable_files)); 868 if (have_files && only_deletable_files) { 869 DEBUG(SCANNEDONLY_DEBUG, 870 ("scannedonly_rmdir, remove leftover scannedonly " 871 "files from %s\n", path_w_slash)); 872 SMB_VFS_NEXT_REWINDDIR(handle, dirp); 873 while ((dire = SMB_VFS_NEXT_READDIR(handle, dirp, NULL)) 874 != NULL) { 875 char *fullpath; 876 struct smb_filename *smb_fname = NULL; 877 if (ISDOT(dire->d_name) || ISDOTDOT(dire->d_name)) { 878 continue; 879 } 880 fullpath = talloc_asprintf(ctx, "%s%s", path_w_slash, 881 dire->d_name); 882 create_synthetic_smb_fname(ctx, fullpath,NULL,NULL, 883 &smb_fname); 884 DEBUG(SCANNEDONLY_DEBUG, ("unlink %s\n", fullpath)); 885 SMB_VFS_NEXT_UNLINK(handle, smb_fname); 886 TALLOC_FREE(fullpath); 887 TALLOC_FREE(smb_fname); 888 } 889 } 890 SMB_VFS_NEXT_CLOSEDIR(handle, dirp); 891 return SMB_VFS_NEXT_RMDIR(handle, path); 892} 893 894static void free_scannedonly_data(void **data) 895{ 896 SAFE_FREE(*data); 897} 898 899static int scannedonly_connect(struct vfs_handle_struct *handle, 900 const char *service, const char *user) 901{ 902 903 struct Tscannedonly *so; 904 905 so = SMB_MALLOC_P(struct Tscannedonly); 906 handle->data = (void *)so; 907 handle->free_data = free_scannedonly_data; 908 so->gsendbuffer[0]='\0'; 909 so->domain_socket = 910 lp_parm_bool(SNUM(handle->conn), "scannedonly", 911 "domain_socket", True); 912 so->socketname = 913 (char *)lp_parm_const_string(SNUM(handle->conn), 914 "scannedonly", "socketname", 915 "/var/lib/scannedonly/scan"); 916 so->portnum = 917 lp_parm_int(SNUM(handle->conn), "scannedonly", "portnum", 918 2020); 919 so->scanhost = 920 (char *)lp_parm_const_string(SNUM(handle->conn), 921 "scannedonly", "scanhost", 922 "localhost"); 923 924 so->show_special_files = 925 lp_parm_bool(SNUM(handle->conn), "scannedonly", 926 "show_special_files", True); 927 so->rm_hidden_files_on_rmdir = 928 lp_parm_bool(SNUM(handle->conn), "scannedonly", 929 "rm_hidden_files_on_rmdir", True); 930 so->hide_nonscanned_files = 931 lp_parm_bool(SNUM(handle->conn), "scannedonly", 932 "hide_nonscanned_files", False); 933 so->allow_nonscanned_files = 934 lp_parm_bool(SNUM(handle->conn), "scannedonly", 935 "allow_nonscanned_files", False); 936 so->scanning_message = 937 (char *)lp_parm_const_string(SNUM(handle->conn), 938 "scannedonly", 939 "scanning_message", 940 "is being scanned for viruses"); 941 so->scanning_message_len = strlen(so->scanning_message); 942 so->recheck_time_open = 943 lp_parm_int(SNUM(handle->conn), "scannedonly", 944 "recheck_time_open", 50); 945 so->recheck_tries_open = 946 lp_parm_int(SNUM(handle->conn), "scannedonly", 947 "recheck_tries_open", 100); 948 so->recheck_size_open = 949 lp_parm_int(SNUM(handle->conn), "scannedonly", 950 "recheck_size_open", 100); 951 so->recheck_time_readdir = 952 lp_parm_int(SNUM(handle->conn), "scannedonly", 953 "recheck_time_readdir", 50); 954 so->recheck_tries_readdir = 955 lp_parm_int(SNUM(handle->conn), "scannedonly", 956 "recheck_tries_readdir", 20); 957 958 so->p_scanned = 959 (char *)lp_parm_const_string(SNUM(handle->conn), 960 "scannedonly", 961 "pref_scanned", 962 ".scanned:"); 963 so->p_virus = 964 (char *)lp_parm_const_string(SNUM(handle->conn), 965 "scannedonly", 966 "pref_virus", 967 ".virus:"); 968 so->p_failed = 969 (char *)lp_parm_const_string(SNUM(handle->conn), 970 "scannedonly", 971 "pref_failed", 972 ".failed:"); 973 connect_to_scanner(handle); 974 975 return SMB_VFS_NEXT_CONNECT(handle, service, user); 976} 977 978/* VFS operations structure */ 979static struct vfs_fn_pointers vfs_scannedonly_fns = { 980 .opendir = scannedonly_opendir, 981 .readdir = scannedonly_readdir, 982 .seekdir = scannedonly_seekdir, 983 .telldir = scannedonly_telldir, 984 .rewind_dir = scannedonly_rewinddir, 985 .closedir = scannedonly_closedir, 986 .rmdir = scannedonly_rmdir, 987 .stat = scannedonly_stat, 988 .lstat = scannedonly_lstat, 989 .open = scannedonly_open, 990 .close_fn = scannedonly_close, 991 .rename = scannedonly_rename, 992 .unlink = scannedonly_unlink, 993 .connect_fn = scannedonly_connect 994}; 995 996NTSTATUS vfs_scannedonly_init(void) 997{ 998 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "scannedonly", 999 &vfs_scannedonly_fns); 1000} 1001