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 + -
显示快捷键?