env_unix.c

来自「这是用C编写IMAP源代码」· C语言 代码 · 共 1,641 行 · 第 1/4 页

C
1,641
字号
/* Return system standard INBOX * Accepts: buffer string */char *sysinbox (){  char tmp[MAILTMPLEN];  if (!sysInbox) {		/* initialize if first time */    sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());    sysInbox = cpystr (tmp);	/* system inbox is from mail spool */  }  return sysInbox;}/* Return mailbox directory name * Accepts: destination buffer *	    directory prefix *	    name in directory * Returns: file name or NIL if error */char *mailboxdir (char *dst,char *dir,char *name){  char tmp[MAILTMPLEN];  if (dir || name) {		/* if either argument provided */    if (dir) {      if (strlen (dir) > NETMAXMBX) return NIL;      strcpy (tmp,dir);		/* write directory prefix */    }    else tmp[0] = '\0';		/* otherwise null string */    if (name) {      if (strlen (name) > NETMAXMBX) return NIL;      strcat (tmp,name);	/* write name in directory */    }				/* validate name, return its name */    if (!mailboxfile (dst,tmp)) return NIL;  }				/* no arguments, wants mailbox directory */  else strcpy (dst,mymailboxdir ());  return dst;			/* return the name */}/* Return mailbox file name * Accepts: destination buffer *	    mailbox name * Returns: file name or empty string for driver-selected INBOX or NIL if error */char *mailboxfile (char *dst,char *name){  struct passwd *pw;  char *s;  if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) ||      ((anonymous || blackBox || restrictBox || (*name == '#')) &&       (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))))    dst = NIL;			/* invalid name */  else switch (*name) {		/* determine mailbox type based upon name */  case '#':			/* namespace name */				/* #ftp/ namespace */    if (((name[1] == 'f') || (name[1] == 'F')) &&	((name[2] == 't') || (name[2] == 'T')) &&	((name[3] == 'p') || (name[3] == 'P')) &&	(name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5);				/* #public/ and #shared/ namespaces */    else if ((((name[1] == 'p') || (name[1] == 'P')) &&	      ((name[2] == 'u') || (name[2] == 'U')) &&	      ((name[3] == 'b') || (name[3] == 'B')) &&	      ((name[4] == 'l') || (name[4] == 'L')) &&	      ((name[5] == 'i') || (name[5] == 'I')) &&	      ((name[6] == 'c') || (name[6] == 'C')) &&	      (name[7] == '/') && (s = publicHome)) ||	     (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&	      ((name[2] == 'h') || (name[2] == 'H')) &&	      ((name[3] == 'a') || (name[3] == 'A')) &&	      ((name[4] == 'r') || (name[4] == 'R')) &&	      ((name[5] == 'e') || (name[5] == 'E')) &&	      ((name[6] == 'd') || (name[6] == 'D')) &&	      (name[7] == '/') && (s = sharedHome)))      sprintf (dst,"%s/%s",s,compare_cstring (name+8,"INBOX") ?	       name+8 : "INBOX");    else dst = NIL;		/* unknown namespace */    break;  case '/':			/* root access */    if (anonymous) dst = NIL;	/* anonymous forbidden to do this */    else if (blackBox) {	/* other user access if blackbox */      if (restrictBox & RESTRICTOTHERUSER) dst = NIL;				/* see if other user INBOX */      else if ((s = strchr (name+1,'/')) && !compare_cstring (s+1,"INBOX")) {	*s = '\0';		/* temporarily tie off string */	sprintf (dst,"%s/%s/INBOX",blackBoxDir,name+1);	*s = '/';		/* in case caller cares */      }      else sprintf (dst,"%s/%s",blackBoxDir,name+1);    }    else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ()))      dst = NIL;		/* restricted and not access to sysinbox */    else strcpy (dst,name);	/* unrestricted, copy root name */    break;  case '~':			/* other user access */				/* bad syntax or anonymous can't win */    if (!*++name || anonymous) dst = NIL;				/* ~/ equivalent to ordinary name */    else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1);				/* other user forbidden if closed/restricted */    else if (closedBox || (restrictBox & RESTRICTOTHERUSER)) dst = NIL;    else if (blackBox) {	/* black box form of other user */				/* see if other user INBOX */      if ((s = strchr (name,'/')) && compare_cstring (s+1,"INBOX")) {	*s = '\0';		/* temporarily tie off string */	sprintf (dst,"%s/%s/INBOX",blackBoxDir,name);	*s = '/';		/* in case caller cares */      }      else sprintf (dst,"%s/%s",blackBoxDir,name);    }    else {			/* clear box other user */				/* copy user name */      for (s = dst; *name && (*name != '/'); *s++ = *name++);      *s++ = '\0';		/* tie off user name, look up in passwd file */      if ((pw = getpwnam (dst)) && pw->pw_dir) {	if (*name) name++;	/* skip past the slash */				/* canonicalize case of INBOX */	if (!compare_cstring (name,"INBOX")) name = "INBOX";				/* remove trailing / from directory */	if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0';				/* don't allow ~root/ if restricted root */	if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL;				/* build final name w/ subdir if needed */	else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name);	else sprintf (dst,"%s/%s",pw->pw_dir,name);      }      else dst = NIL;		/* no such user */    }    break;  case 'I': case 'i':		/* possible INBOX */    if (!compare_cstring (name+1,"NBOX")) {				/* if restricted, use INBOX in mailbox dir */      if (anonymous || blackBox || closedBox)	sprintf (dst,"%s/INBOX",mymailboxdir ());      else *dst = '\0';		/* otherwise driver selects the name */      break;    }				/* drop into to ordinary name case */  default:			/* ordinary name is easy */    sprintf (dst,"%s/%s",mymailboxdir (),name);    break;  }  return dst;			/* return final name */}/* Dot-lock file locker * Accepts: file name to lock *	    destination buffer for lock file name *	    open file description on file name to lock * Returns: T if success, NIL if failure */long dotlock_lock (char *file,DOTLOCK *base,int fd){  int i = locktimeout * 60;  int j,mask,retry,pi[2],po[2];  char *s,tmp[MAILTMPLEN];  struct stat sb;				/* flush absurd file name */  if (strlen (file) > 512) return NIL;				/* build lock filename */  sprintf (base->lock,"%s.lock",file);				/* assume no pipe */  base->pipei = base->pipeo = -1;  do {				/* make sure not symlink */    if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;				/* time out if file older than 5 minutes */    if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0;				/* try to create the lock */    switch (retry = crexcl (base->lock)) {    case -1:			/* OK to retry */      if (!(i%15)) {		/* time to notify? */	sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",		 file,i);	MM_LOG (tmp,WARN);      }      sleep (1);		/* wait 1 second before next try */      break;    case NIL:			/* failure, can't retry */      i = 0;      break;    case T:			/* success, make sure others can break lock */      chmod (base->lock,(int) lock_protection);      return LONGT;    }  } while (i--);		/* until out of retries */  if (retry < 0) {		/* still returning retry after locktimeout? */    if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;    if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) {      sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock",	       (long) (time (0) - sb.st_ctime));      MM_LOG (tmp,WARN);    }    mask = umask (0);		/* want our lock protection */    unlink (base->lock);	/* try to remove the old file */				/* seize the lock */    if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) lock_protection)) >= 0) {      close (i);		/* don't need descriptor any more */      sprintf (tmp,"Mailbox %.80s lock overridden",file);      MM_LOG (tmp,NIL);      chmod (base->lock,(int) lock_protection);      umask (mask);		/* restore old umask */      return LONGT;    }    umask (mask);		/* restore old umask */  }  if (fd >= 0) switch (errno) {  case EACCES:			/* protection failure? */				/* make command pipes */    if (!closedBox && !stat (LOCKPGM,&sb) && (pipe (pi) >= 0)) {      if (pipe (po) >= 0) {	if (!(j = fork ())) {	/* make inferior process */	  if (!fork ()) {	/* make grandchild so it's inherited by init */	    char *argv[4];				/* prepare argument vector */	    sprintf (tmp,"%d",fd);	    argv[0] = LOCKPGM; argv[1] = tmp;	    argv[2] = file; argv[3] = NIL;				/* set parent's I/O to my O/I */	    dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);				/* close all unnecessary descriptors */	    for (j = max (20,max (max (pi[0],pi[1]),max(po[0],po[1])));		 j >= 3; --j) if (j != fd) close (j);				/* be our own process group */	    setpgrp (0,getpid ());				/* now run it */	    execv (argv[0],argv);	  }	  _exit (1);		/* child is done */	}	else if (j > 0) {	/* reap child; grandchild now owned by init */	  grim_pid_reap (j,NIL);				/* read response from locking program */	  if ((read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {				/* success, record pipes */	    base->pipei = pi[0]; base->pipeo = po[1];				/* close child's side of the pipes */	    close (pi[1]); close (po[0]);	    return LONGT;	  }	}	close (po[0]); close (po[1]);      }      close (pi[0]); close (pi[1]);    }				/* find directory/file delimiter */    if (s = strrchr (base->lock,'/')) {      *s = '\0';		/* tie off at directory */      sprintf(tmp,		/* generate default message */	      "Mailbox vulnerable - directory %.80s must have 1777 protection",	      base->lock);				/* definitely not 1777 if can't stat */      mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777);      *s = '/';			/* restore lock name */      if (mask != 1777) {	/* default warning if not 1777 */	if (!disableLockWarning) MM_LOG (tmp,WARN);	break;      }    }  default:    sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",	     base->lock,strerror (errno));    if (!disableLockWarning) MM_LOG (tmp,WARN);    break;  }  base->lock[0] = '\0';		/* don't use lock files */  return NIL;}/* Dot-lock file unlocker * Accepts: lock file name * Returns: T if success, NIL if failure */long dotlock_unlock (DOTLOCK *base){  long ret = LONGT;  if (base && base->lock[0]) {    if (base->pipei >= 0) {	/* if running through a pipe unlocker */      ret = (write (base->pipeo,"+",1) == 1);				/* nuke the pipes */      close (base->pipei); close (base->pipeo);    }    else ret = !unlink (base->lock);  }  return ret;}/* Lock file name * Accepts: scratch buffer *	    file name *	    type of locking operation (LOCK_SH or LOCK_EX) *	    pointer to return PID of locker * Returns: file descriptor of lock or negative if error */int lockname (char *lock,char *fname,int op,long *pid){  struct stat sbuf;  *pid = 0;			/* no locker PID */  return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);}/* Lock file descriptor * Accepts: file descriptor *	    lock file name buffer *	    type of locking operation (LOCK_SH or LOCK_EX) * Returns: file descriptor of lock or negative if error */int lockfd (int fd,char *lock,int op){  struct stat sbuf;  return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);}/* Lock file name worker * Accepts: lock file name *	    pointer to stat() buffer *	    type of locking operation (LOCK_SH or LOCK_EX) *	    pointer to return PID of locker * Returns: file descriptor of lock or negative if error */int lock_work (char *lock,void *sb,int op,long *pid){  struct stat lsb,fsb;  struct stat *sbuf = (struct stat *) sb;  char tmp[MAILTMPLEN];  long i;  int fd;  int mask = umask (0);  if (pid) *pid = 0;		/* initialize return PID */				/* make temporary lock file name */  sprintf (lock,"%s/.%lx.%lx",closedBox ? "" : "/tmp",	   (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino);  while (T) {			/* until get a good lock */    do switch ((int) chk_notsymlink (lock,&lsb)) {    case 1:			/* exists just once */      if (((fd = open (lock,O_RDWR,lock_protection)) >= 0) ||	  (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;    case -1:			/* name doesn't exist */      fd = open (lock,O_RDWR|O_CREAT|O_EXCL,lock_protection);      break;    default:			/* multiple hard links */      MM_LOG ("hard link to lock name",ERROR);      syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);    case 0:			/* symlink (already did syslog) */      umask (mask);		/* restore old mask */      return -1;		/* fail: no lock file */    } while ((fd < 0) && (errno == EEXIST));    if (fd < 0) {		/* failed to get file descriptor */      syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,	      strerror (errno));      if (!closedBox) {		/* more explicit snarl for bad configuration */	if (stat ("/tmp",&lsb))	  syslog (LOG_CRIT,"SYSTEM ERROR: no /tmp: %s",strerror (errno));	else if ((lsb.st_mode & 01777) != 01777)	  MM_LOG ("Can't lock for write: /tmp must have 1777 protection",WARN);      }      umask (mask);		/* restore old mask */      return -1;		/* fail: can't open lock file */    }				/* non-blocking form */    if (op & LOCK_NB) i = flock (fd,op);    else {			/* blocking form */      (*mailblocknotify) (BLOCK_FILELOCK,NIL);      i = flock (fd,op);      (*mailblocknotify) (BLOCK_NONE,NIL);    }    if (i) {			/* failed, get other process' PID */      if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&	  (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))	*pid = i;      close (fd);		/* failed, give up on lock */      umask (mask);		/* restore old mask */      return -1;		/* fail: can't lock */    }				/* make sure this lock is good for us */    if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&	!fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&	(lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;    close (fd);			/* lock not right, drop fd and try again */  }				/* make sure mode OK (don't use fchmod()) */  chmod (lock,(int) lock_protection);  umask (mask);			/* restore old mask */  return fd;			/* success */}/* Check to make sure not a symlink * Accepts: file name *	    stat buffer * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links */long chk_notsymlink (char *name,void *sb){  struct stat *sbuf = (struct stat *) sb;				/* name exists? */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?