yuuji@0: /* ======================================================================== yuuji@0: * Copyright 1988-2008 University of Washington yuuji@0: * yuuji@0: * Licensed under the Apache License, Version 2.0 (the "License"); yuuji@0: * you may not use this file except in compliance with the License. yuuji@0: * You may obtain a copy of the License at yuuji@0: * yuuji@0: * http://www.apache.org/licenses/LICENSE-2.0 yuuji@0: * yuuji@0: * yuuji@0: * ======================================================================== yuuji@0: */ yuuji@0: yuuji@0: /* yuuji@0: * Program: UNIX environment routines yuuji@0: * yuuji@0: * Author: Mark Crispin yuuji@0: * UW Technology yuuji@0: * University of Washington yuuji@0: * Seattle, WA 98195 yuuji@0: * Internet: MRC@Washington.EDU yuuji@0: * yuuji@0: * Date: 1 August 1988 yuuji@3: * Last Edited: 23 February 2009 yuuji@0: */ yuuji@0: yuuji@0: #include yuuji@0: #include yuuji@0: #include yuuji@0: yuuji@0: yuuji@0: /* in case stat.h is ancient */ yuuji@0: yuuji@0: #ifndef S_IRUSR yuuji@0: #define S_IRUSR S_IREAD yuuji@0: #endif yuuji@0: #ifndef S_IWUSR yuuji@0: #define S_IWUSR S_IWRITE yuuji@0: #endif yuuji@0: #ifndef S_IXUSR yuuji@0: #define S_IXUSR S_IEXEC yuuji@0: #endif yuuji@0: #ifndef S_IRGRP yuuji@0: #define S_IRGRP (S_IREAD >> 3) yuuji@0: #endif yuuji@0: #ifndef S_IWGRP yuuji@0: #define S_IWGRP (S_IWRITE >> 3) yuuji@0: #endif yuuji@0: #ifndef S_IXGRP yuuji@0: #define S_IXGRP (S_IEXEC >> 3) yuuji@0: #endif yuuji@0: #ifndef S_IROTH yuuji@0: #define S_IROTH (S_IREAD >> 6) yuuji@0: #endif yuuji@0: #ifndef S_IWOTH yuuji@0: #define S_IWOTH (S_IWRITE >> 6) yuuji@0: #endif yuuji@0: #ifndef S_IXOTH yuuji@0: #define S_IXOTH (S_IEXEC >> 6) yuuji@0: #endif yuuji@0: yuuji@0: /* c-client environment parameters */ yuuji@0: yuuji@0: static char *myUserName = NIL; /* user name */ yuuji@0: static char *myHomeDir = NIL; /* home directory name */ yuuji@0: static char *myServerName = NIL;/* server name */ yuuji@0: static char *myLocalHost = NIL; /* local host name */ yuuji@0: static char *myNewsrc = NIL; /* newsrc file name */ yuuji@0: static char *mailsubdir = NIL; /* mailbox subdirectory name */ yuuji@0: static char *sysInbox = NIL; /* system inbox name */ yuuji@0: static char *newsActive = NIL; /* news active file */ yuuji@0: static char *newsSpool = NIL; /* news spool */ yuuji@0: static char *blackBoxDir = NIL; /* black box directory name */ yuuji@0: /* black box default home directory */ yuuji@0: static char *blackBoxDefaultHome = NIL; yuuji@0: static char *sslCApath = NIL; /* non-standard CA path */ yuuji@0: static short anonymous = NIL; /* is anonymous */ yuuji@0: static short blackBox = NIL; /* is a black box */ yuuji@0: static short closedBox = NIL; /* is a closed box (uses chroot() jail) */ yuuji@0: static short restrictBox = NIL; /* is a restricted box */ yuuji@0: static short has_no_life = NIL; /* is a cretin with no life */ yuuji@0: /* block environment init */ yuuji@0: static short block_env_init = NIL; yuuji@0: static short hideDotFiles = NIL;/* hide files whose names start with . */ yuuji@0: /* advertise filesystem root */ yuuji@0: static short advertisetheworld = NIL; yuuji@0: /* only advertise own mailboxes and #shared */ yuuji@0: static short limitedadvertise = NIL; yuuji@0: /* disable automatic shared namespaces */ yuuji@0: static short noautomaticsharedns = NIL; yuuji@0: static short no822tztext = NIL; /* disable RFC [2]822 timezone text */ yuuji@0: /* client principals include service name */ yuuji@0: static short kerb_cp_svr_name = NIL; yuuji@0: static long locktimeout = 5; /* default lock timeout in minutes */ yuuji@0: /* default prototypes */ yuuji@0: static MAILSTREAM *createProto = NIL; yuuji@0: static MAILSTREAM *appendProto = NIL; yuuji@0: /* default user flags */ yuuji@0: static char *userFlags[NUSERFLAGS] = {NIL}; yuuji@0: static NAMESPACE *nslist[3]; /* namespace list */ yuuji@0: static int logtry = 3; /* number of server login tries */ yuuji@0: /* block notification */ yuuji@0: static blocknotify_t mailblocknotify = mm_blocknotify; yuuji@0: /* logout function */ yuuji@0: static logouthook_t maillogouthook = NIL; yuuji@0: /* logout data */ yuuji@0: static void *maillogoutdata = NIL; yuuji@0: /* allow user config files */ yuuji@0: static short allowuserconfig = NIL; yuuji@0: /* 1 = disable plaintext, 2 = if not SSL */ yuuji@0: static long disablePlaintext = NIL; yuuji@0: static long list_max_level = 20;/* maximum level of list recursion */ yuuji@0: /* facility for syslog */ yuuji@0: static int syslog_facility = LOG_MAIL; yuuji@0: yuuji@0: /* Path of the privileged system lock program (mlock). Normally set by yuuji@0: * logic test. yuuji@0: */ yuuji@0: yuuji@0: static char *lockpgm = LOCKPGM; yuuji@0: yuuji@0: /* Directory used for shared locks. MUST be the same for all users of the yuuji@0: * system, and MUST be protected 1777. /var/tmp may be preferable on some yuuji@0: * systems. yuuji@0: */ yuuji@0: yuuji@0: static const char *tmpdir = "/tmp"; yuuji@0: yuuji@0: /* Do not change shlock_mode. Doing so can cause mailbox corruption and yuuji@0: * denial of service. It also defeats the entire purpose of the shared yuuji@0: * lock mechanism. The right way to avoid shared locks is to set up a yuuji@0: * closed box (see the closedBox setting). yuuji@0: */ yuuji@0: yuuji@0: /* shared lock mode */ yuuji@0: static const int shlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; yuuji@0: yuuji@0: yuuji@0: /* It is STRONGLY recommended that you do not change dotlock_mode. Doing so yuuji@0: * can cause denial of service with old dot-lock files left lying around. yuuji@0: * However, since dot-locks are only used with traditional UNIX and MMDF yuuji@0: * formats which are not normally shared, it is much less harmful to tamper yuuji@0: * with this than with shlock_mode. yuuji@0: */ yuuji@0: yuuji@0: /* dot-lock mode */ yuuji@0: static long dotlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; yuuji@0: yuuji@0: /* File/directory access and protection policies */ yuuji@0: yuuji@0: /* Unlike shlock_mode, the ????_protection modes are intended to be fully yuuji@0: * customizable according to site policy. The values here are recommended yuuji@0: * settings, based upon the documented purposes of the namespaces. yuuji@0: */ yuuji@0: yuuji@0: /* user space - only owner can read/write */ yuuji@0: static char *myMailboxDir = NIL;/* user space directory name */ yuuji@0: /* default file protection */ yuuji@0: static long mbx_protection = S_IRUSR|S_IWUSR; yuuji@0: /* default directory protection */ yuuji@0: static long dir_protection = S_IRUSR|S_IWUSR|S_IXUSR; yuuji@0: yuuji@0: /* user space for user "anonymous" */ yuuji@0: /* anonymous home directory */ yuuji@0: static char *anonymousHome = NIL; yuuji@0: yuuji@0: /* #ftp - everybody can read, only owner can write */ yuuji@0: static char *ftpHome = NIL; /* ftp export home directory */ yuuji@0: /* default ftp file protection */ yuuji@0: static long ftp_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; yuuji@0: static long ftp_dir_protection =/* default ftp directory protection */ yuuji@0: S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; yuuji@0: yuuji@0: /* #public - everybody can read/write */ yuuji@0: static char *publicHome = NIL; /* public home directory */ yuuji@0: static long public_protection = /* default public file protection */ yuuji@0: S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; yuuji@0: /* default public directory protection */ yuuji@0: static long public_dir_protection = yuuji@0: S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH; yuuji@0: yuuji@0: /* #shared/ - owner and group members can read/write */ yuuji@0: static char *sharedHome = NIL; /* shared home directory */ yuuji@0: /* default shared file protection */ yuuji@0: static long shared_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP; yuuji@0: /* default shared directory protection */ yuuji@0: static long shared_dir_protection = yuuji@0: S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP; yuuji@0: yuuji@0: /* OS bug workarounds - should be avoided at all cost */ yuuji@0: yuuji@0: yuuji@0: /* Don't set fcntlhangbug unless you really have to, since it risks mailbox yuuji@0: * corruption. The flocksim.c mechanism is designed to detect NFS access yuuji@0: * and no-op in that cases only, so this flag should be unnecessary. yuuji@0: */ yuuji@0: yuuji@0: static short fcntlhangbug = NIL;/* flock() emulator using fcntl() is a no-op */ yuuji@0: yuuji@0: yuuji@0: /* Don't set netfsstatbug unless you really have to, since it dramatically yuuji@0: * slows down traditional UNIX and MMDF mailbox performance. yuuji@0: */ yuuji@0: yuuji@0: static short netfsstatbug = NIL;/* compensate for broken stat() on network yuuji@0: * filesystems (AFS and old NFS) yuuji@0: */ yuuji@0: yuuji@0: yuuji@0: /* Note: setting disableLockWarning means that you assert that the yuuji@0: * so-modified copy of this software will NEVER be used: yuuji@0: * 1) in conjunction with any software which expects .lock files yuuji@0: * 2) to access NFS-mounted files and directories yuuji@0: * yuuji@0: * Unless both of these conditions apply, then do not set this flag. yuuji@0: * Instead, read the FAQ (item 7.10) and either use 1777 protection yuuji@0: * on the mail spool, or install mlock. yuuji@0: * yuuji@0: * In addition, by setting this flag you also agree that you are fully yuuji@0: * legally and morally responsible when (not if) mail files are damaged yuuji@0: * as the result of your choice. yuuji@0: * yuuji@0: * The mlock tool exists for a reason. Use it. yuuji@0: */ yuuji@0: /* disable warning if can't make .lock file */ yuuji@0: static short disableLockWarning = NIL; yuuji@0: yuuji@0: /* UNIX Namespaces */ yuuji@0: yuuji@0: /* personal mh namespace */ yuuji@0: static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL}; yuuji@0: static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf}; yuuji@0: /* home namespace */ yuuji@0: static NAMESPACE nshome = {"",'/',NIL,&nsmh}; yuuji@0: /* UNIX other user namespace */ yuuji@0: static NAMESPACE nsunixother = {"~",'/',NIL,NIL}; yuuji@0: /* black box other user namespace */ yuuji@0: static NAMESPACE nsblackother = {"/",'/',NIL,NIL}; yuuji@0: /* public (anonymous OK) namespace */ yuuji@0: static NAMESPACE nspublic = {"#public/",'/',NIL,NIL}; yuuji@0: /* netnews namespace */ yuuji@0: static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic}; yuuji@0: /* FTP export namespace */ yuuji@0: static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews}; yuuji@0: /* shared (no anonymous) namespace */ yuuji@0: static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp}; yuuji@0: /* world namespace */ yuuji@0: static NAMESPACE nsworld = {"/",'/',NIL,&nsshared}; yuuji@0: /* only shared and public namespaces */ yuuji@0: static NAMESPACE nslimited = {"#shared/",'/',NIL,&nspublic}; yuuji@0: yuuji@0: yuuji@0: yuuji@0: #include "write.c" /* include safe writing routines */ yuuji@0: #include "crexcl.c" /* include exclusive create */ yuuji@0: #include "pmatch.c" /* include wildcard pattern matcher */ yuuji@0: yuuji@0: /* Get all authenticators */ yuuji@0: yuuji@0: #include "auths.c" yuuji@0: yuuji@0: /* Environment manipulate parameters yuuji@0: * Accepts: function code yuuji@0: * function-dependent value yuuji@0: * Returns: function-dependent return value yuuji@0: */ yuuji@0: yuuji@0: void *env_parameters (long function,void *value) yuuji@0: { yuuji@0: void *ret = NIL; yuuji@0: switch ((int) function) { yuuji@0: case GET_NAMESPACE: yuuji@0: ret = (void *) nslist; yuuji@0: break; yuuji@0: case SET_USERNAME: yuuji@0: if (myUserName) fs_give ((void **) &myUserName); yuuji@0: myUserName = cpystr ((char *) value); yuuji@0: case GET_USERNAME: yuuji@0: ret = (void *) myUserName; yuuji@0: break; yuuji@0: case SET_HOMEDIR: yuuji@0: if (myHomeDir) fs_give ((void **) &myHomeDir); yuuji@0: myHomeDir = cpystr ((char *) value); yuuji@0: case GET_HOMEDIR: yuuji@0: ret = (void *) myHomeDir; yuuji@0: break; yuuji@0: case SET_LOCALHOST: yuuji@0: if (myLocalHost) fs_give ((void **) &myLocalHost); yuuji@0: myLocalHost = cpystr ((char *) value); yuuji@0: case GET_LOCALHOST: yuuji@0: ret = (void *) myLocalHost; yuuji@0: break; yuuji@0: case SET_NEWSRC: yuuji@0: if (myNewsrc) fs_give ((void **) &myNewsrc); yuuji@0: myNewsrc = cpystr ((char *) value); yuuji@0: case GET_NEWSRC: yuuji@0: ret = (void *) myNewsrc; yuuji@0: break; yuuji@0: case SET_NEWSACTIVE: yuuji@0: if (newsActive) fs_give ((void **) &newsActive); yuuji@0: newsActive = cpystr ((char *) value); yuuji@0: case GET_NEWSACTIVE: yuuji@0: ret = (void *) newsActive; yuuji@0: break; yuuji@0: case SET_NEWSSPOOL: yuuji@0: if (newsSpool) fs_give ((void **) &newsSpool); yuuji@0: newsSpool = cpystr ((char *) value); yuuji@0: case GET_NEWSSPOOL: yuuji@0: ret = (void *) newsSpool; yuuji@0: break; yuuji@0: yuuji@0: case SET_ANONYMOUSHOME: yuuji@0: if (anonymousHome) fs_give ((void **) &anonymousHome); yuuji@0: anonymousHome = cpystr ((char *) value); yuuji@0: case GET_ANONYMOUSHOME: yuuji@0: if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME); yuuji@0: ret = (void *) anonymousHome; yuuji@0: break; yuuji@0: case SET_FTPHOME: yuuji@0: if (ftpHome) fs_give ((void **) &ftpHome); yuuji@0: ftpHome = cpystr ((char *) value); yuuji@0: case GET_FTPHOME: yuuji@0: ret = (void *) ftpHome; yuuji@0: break; yuuji@0: case SET_PUBLICHOME: yuuji@0: if (publicHome) fs_give ((void **) &publicHome); yuuji@0: publicHome = cpystr ((char *) value); yuuji@0: case GET_PUBLICHOME: yuuji@0: ret = (void *) publicHome; yuuji@0: break; yuuji@0: case SET_SHAREDHOME: yuuji@0: if (sharedHome) fs_give ((void **) &sharedHome); yuuji@0: sharedHome = cpystr ((char *) value); yuuji@0: case GET_SHAREDHOME: yuuji@0: ret = (void *) sharedHome; yuuji@0: break; yuuji@0: case SET_SYSINBOX: yuuji@0: if (sysInbox) fs_give ((void **) &sysInbox); yuuji@0: sysInbox = cpystr ((char *) value); yuuji@0: case GET_SYSINBOX: yuuji@0: ret = (void *) sysInbox; yuuji@0: break; yuuji@0: case SET_SSLCAPATH: /* this can be set null */ yuuji@0: if (sslCApath) fs_give ((void **) &sslCApath); yuuji@0: sslCApath = value ? cpystr ((char *) value) : value; yuuji@0: break; yuuji@0: case GET_SSLCAPATH: yuuji@0: ret = (void *) sslCApath; yuuji@0: break; yuuji@0: case SET_LISTMAXLEVEL: yuuji@0: list_max_level = (long) value; yuuji@0: case GET_LISTMAXLEVEL: yuuji@0: ret = (void *) list_max_level; yuuji@0: break; yuuji@0: yuuji@0: case SET_MBXPROTECTION: yuuji@0: mbx_protection = (long) value; yuuji@0: case GET_MBXPROTECTION: yuuji@0: ret = (void *) mbx_protection; yuuji@0: break; yuuji@0: case SET_DIRPROTECTION: yuuji@0: dir_protection = (long) value; yuuji@0: case GET_DIRPROTECTION: yuuji@0: ret = (void *) dir_protection; yuuji@0: break; yuuji@0: case SET_LOCKPROTECTION: yuuji@0: dotlock_mode = (long) value; yuuji@0: case GET_LOCKPROTECTION: yuuji@0: ret = (void *) dotlock_mode; yuuji@0: break; yuuji@0: case SET_FTPPROTECTION: yuuji@0: ftp_protection = (long) value; yuuji@0: case GET_FTPPROTECTION: yuuji@0: ret = (void *) ftp_protection; yuuji@0: break; yuuji@0: case SET_PUBLICPROTECTION: yuuji@0: public_protection = (long) value; yuuji@0: case GET_PUBLICPROTECTION: yuuji@0: ret = (void *) public_protection; yuuji@0: break; yuuji@0: case SET_SHAREDPROTECTION: yuuji@0: shared_protection = (long) value; yuuji@0: case GET_SHAREDPROTECTION: yuuji@0: ret = (void *) shared_protection; yuuji@0: break; yuuji@0: case SET_FTPDIRPROTECTION: yuuji@0: ftp_dir_protection = (long) value; yuuji@0: case GET_FTPDIRPROTECTION: yuuji@0: ret = (void *) ftp_dir_protection; yuuji@0: break; yuuji@0: case SET_PUBLICDIRPROTECTION: yuuji@0: public_dir_protection = (long) value; yuuji@0: case GET_PUBLICDIRPROTECTION: yuuji@0: ret = (void *) public_dir_protection; yuuji@0: break; yuuji@0: case SET_SHAREDDIRPROTECTION: yuuji@0: shared_dir_protection = (long) value; yuuji@0: case GET_SHAREDDIRPROTECTION: yuuji@0: ret = (void *) shared_dir_protection; yuuji@0: break; yuuji@0: yuuji@0: case SET_LOCKTIMEOUT: yuuji@0: locktimeout = (long) value; yuuji@0: case GET_LOCKTIMEOUT: yuuji@0: ret = (void *) locktimeout; yuuji@0: break; yuuji@0: case SET_DISABLEFCNTLLOCK: yuuji@0: fcntlhangbug = value ? T : NIL; yuuji@0: case GET_DISABLEFCNTLLOCK: yuuji@0: ret = (void *) (fcntlhangbug ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_LOCKEACCESERROR: yuuji@0: disableLockWarning = value ? NIL : T; yuuji@0: case GET_LOCKEACCESERROR: yuuji@0: ret = (void *) (disableLockWarning ? NIL : VOIDT); yuuji@0: break; yuuji@0: case SET_HIDEDOTFILES: yuuji@0: hideDotFiles = value ? T : NIL; yuuji@0: case GET_HIDEDOTFILES: yuuji@0: ret = (void *) (hideDotFiles ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_DISABLEPLAINTEXT: yuuji@0: disablePlaintext = (long) value; yuuji@0: case GET_DISABLEPLAINTEXT: yuuji@4: #ifdef RESTRICT_POP yuuji@4: if (getenv("INTRANET") == NIL) disablePlaintext = 1; yuuji@4: else disablePlaintext = NIL; yuuji@4: #endif yuuji@0: ret = (void *) disablePlaintext; yuuji@0: break; yuuji@0: case SET_CHROOTSERVER: yuuji@0: closedBox = value ? T : NIL; yuuji@0: case GET_CHROOTSERVER: yuuji@0: ret = (void *) (closedBox ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_ADVERTISETHEWORLD: yuuji@0: advertisetheworld = value ? T : NIL; yuuji@0: case GET_ADVERTISETHEWORLD: yuuji@0: ret = (void *) (advertisetheworld ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_LIMITEDADVERTISE: yuuji@0: limitedadvertise = value ? T : NIL; yuuji@0: case GET_LIMITEDADVERTISE: yuuji@0: ret = (void *) (limitedadvertise ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_DISABLEAUTOSHAREDNS: yuuji@0: noautomaticsharedns = value ? T : NIL; yuuji@0: case GET_DISABLEAUTOSHAREDNS: yuuji@0: ret = (void *) (noautomaticsharedns ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_DISABLE822TZTEXT: yuuji@0: no822tztext = value ? T : NIL; yuuji@0: case GET_DISABLE822TZTEXT: yuuji@0: ret = (void *) (no822tztext ? VOIDT : NIL); yuuji@0: break; yuuji@0: yuuji@0: case SET_USERHASNOLIFE: yuuji@0: has_no_life = value ? T : NIL; yuuji@0: case GET_USERHASNOLIFE: yuuji@0: ret = (void *) (has_no_life ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_KERBEROS_CP_SVR_NAME: yuuji@0: kerb_cp_svr_name = value ? T : NIL; yuuji@0: case GET_KERBEROS_CP_SVR_NAME: yuuji@0: ret = (void *) (kerb_cp_svr_name ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_NETFSSTATBUG: yuuji@0: netfsstatbug = value ? T : NIL; yuuji@0: case GET_NETFSSTATBUG: yuuji@0: ret = (void *) (netfsstatbug ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_BLOCKENVINIT: yuuji@0: block_env_init = value ? T : NIL; yuuji@0: case GET_BLOCKENVINIT: yuuji@0: ret = (void *) (block_env_init ? VOIDT : NIL); yuuji@0: break; yuuji@0: case SET_BLOCKNOTIFY: yuuji@0: mailblocknotify = (blocknotify_t) value; yuuji@0: case GET_BLOCKNOTIFY: yuuji@0: ret = (void *) mailblocknotify; yuuji@0: break; yuuji@0: case SET_LOGOUTHOOK: yuuji@0: maillogouthook = (logouthook_t) value; yuuji@0: case GET_LOGOUTHOOK: yuuji@0: ret = maillogouthook; yuuji@0: break; yuuji@0: case SET_LOGOUTDATA: yuuji@0: maillogoutdata = (void *) value; yuuji@0: case GET_LOGOUTDATA: yuuji@0: ret = maillogoutdata; yuuji@0: } yuuji@0: return ret; yuuji@0: } yuuji@0: yuuji@0: /* Write current time yuuji@0: * Accepts: destination string yuuji@0: * optional format of day-of-week prefix yuuji@0: * format of date and time yuuji@0: * flag whether to append symbolic timezone yuuji@0: */ yuuji@0: yuuji@0: static void do_date (char *date,char *prefix,char *fmt,int suffix) yuuji@0: { yuuji@0: time_t tn = time (0); yuuji@0: struct tm *t = gmtime (&tn); yuuji@0: int zone = t->tm_hour * 60 + t->tm_min; yuuji@0: int julian = t->tm_yday; yuuji@0: t = localtime (&tn); /* get local time now */ yuuji@0: /* minus UTC minutes since midnight */ yuuji@0: zone = t->tm_hour * 60 + t->tm_min - zone; yuuji@0: /* julian can be one of: yuuji@0: * 36x local time is December 31, UTC is January 1, offset -24 hours yuuji@0: * 1 local time is 1 day ahead of UTC, offset +24 hours yuuji@0: * 0 local time is same day as UTC, no offset yuuji@0: * -1 local time is 1 day behind UTC, offset -24 hours yuuji@0: * -36x local time is January 1, UTC is December 31, offset +24 hours yuuji@0: */ yuuji@0: if (julian = t->tm_yday -julian) yuuji@0: zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60; yuuji@0: if (prefix) { /* want day of week? */ yuuji@0: sprintf (date,prefix,days[t->tm_wday]); yuuji@0: date += strlen (date); /* make next sprintf append */ yuuji@0: } yuuji@0: /* output the date */ yuuji@0: sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900, yuuji@0: t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60); yuuji@0: /* append timezone suffix if desired */ yuuji@0: if (suffix) rfc822_timezone (date,(void *) t); yuuji@0: } yuuji@0: yuuji@0: /* Write current time in RFC 822 format yuuji@0: * Accepts: destination string yuuji@0: */ yuuji@0: yuuji@0: void rfc822_date (char *date) yuuji@0: { yuuji@0: do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d", yuuji@0: no822tztext ? NIL : T); yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Write current time in fixed-width RFC 822 format yuuji@0: * Accepts: destination string yuuji@0: */ yuuji@0: yuuji@0: void rfc822_fixed_date (char *date) yuuji@0: { yuuji@0: do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL); yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Write current time in internal format yuuji@0: * Accepts: destination string yuuji@0: */ yuuji@0: yuuji@0: void internal_date (char *date) yuuji@0: { yuuji@0: do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL); yuuji@0: } yuuji@0: yuuji@0: /* Initialize server yuuji@0: * Accepts: server name for syslog or NIL yuuji@0: * /etc/services service name or NIL yuuji@0: * alternate /etc/services service name or NIL yuuji@0: * clock interrupt handler yuuji@0: * kiss-of-death interrupt handler yuuji@0: * hangup interrupt handler yuuji@0: * termination interrupt handler yuuji@0: */ yuuji@0: yuuji@0: void server_init (char *server,char *service,char *sslservice, yuuji@0: void *clkint,void *kodint,void *hupint,void *trmint, yuuji@0: void *staint) yuuji@0: { yuuji@0: int onceonly = server && service && sslservice; yuuji@0: if (onceonly) { /* set server name in syslog */ yuuji@0: int mask; yuuji@0: openlog (myServerName = cpystr (server),LOG_PID,syslog_facility); yuuji@0: fclose (stderr); /* possibly save a process ID */ yuuji@0: dorc (NIL,NIL); /* do systemwide configuration */ yuuji@0: switch (mask = umask (022)){/* check old umask */ yuuji@0: case 0: /* definitely unreasonable */ yuuji@0: case 022: /* don't need to change it */ yuuji@0: break; yuuji@0: default: /* already was a reasonable value */ yuuji@0: umask (mask); /* so change it back */ yuuji@0: } yuuji@0: } yuuji@0: arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */ yuuji@0: arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */ yuuji@0: arm_signal (SIGHUP,hupint); /* prepare for hangup */ yuuji@0: arm_signal (SIGPIPE,hupint); /* alternative hangup */ yuuji@0: arm_signal (SIGTERM,trmint); /* prepare for termination */ yuuji@0: /* status dump */ yuuji@0: if (staint) arm_signal (SIGUSR1,staint); yuuji@0: if (onceonly) { /* set up network and maybe SSL */ yuuji@0: long port; yuuji@0: struct servent *sv; yuuji@0: /* Use SSL if SSL service, or if server starts with "s" and not service */ yuuji@0: if (((port = tcp_serverport ()) >= 0)) { yuuji@0: if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port))) yuuji@0: syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ()); yuuji@0: else if ((sv = getservbyname (sslservice,"tcp")) && yuuji@0: (port == ntohs (sv->s_port))) { yuuji@0: syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice, yuuji@0: tcp_clientaddr ()); yuuji@0: ssl_server_init (server); yuuji@0: } yuuji@0: else { /* not service or SSL service port */ yuuji@0: syslog (LOG_DEBUG,"port %ld service init from %s",port, yuuji@0: tcp_clientaddr ()); yuuji@0: if (*server == 's') ssl_server_init (server); yuuji@0: } yuuji@0: } yuuji@0: } yuuji@0: } yuuji@0: yuuji@0: /* Wait for stdin input yuuji@0: * Accepts: timeout in seconds yuuji@0: * Returns: T if have input on stdin, else NIL yuuji@0: */ yuuji@0: yuuji@0: long server_input_wait (long seconds) yuuji@0: { yuuji@0: fd_set rfd,efd; yuuji@0: struct timeval tmo; yuuji@0: FD_ZERO (&rfd); yuuji@0: FD_ZERO (&efd); yuuji@0: FD_SET (0,&rfd); yuuji@0: FD_SET (0,&efd); yuuji@0: tmo.tv_sec = seconds; tmo.tv_usec = 0; yuuji@0: return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL; yuuji@0: } yuuji@0: yuuji@0: /* Return UNIX password entry for user name yuuji@0: * Accepts: user name string yuuji@0: * Returns: password entry yuuji@0: * yuuji@0: * Tries all-lowercase form of user name if given user name fails yuuji@0: */ yuuji@0: yuuji@0: static struct passwd *pwuser (unsigned char *user) yuuji@0: { yuuji@0: unsigned char *s; yuuji@0: struct passwd *pw = getpwnam (user); yuuji@0: if (!pw) { /* failed, see if any uppercase characters */ yuuji@0: for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++); yuuji@0: if (*s) { /* yes, try all lowercase form */ yuuji@0: pw = getpwnam (s = lcase (cpystr (user))); yuuji@0: fs_give ((void **) &s); yuuji@0: } yuuji@0: } yuuji@0: return pw; yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Validate password for user name yuuji@0: * Accepts: user name string yuuji@0: * password string yuuji@0: * argument count yuuji@0: * argument vector yuuji@0: * Returns: password entry if validated yuuji@0: * yuuji@0: * Tries password+1 if password fails and starts with space yuuji@0: */ yuuji@0: yuuji@0: static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[]) yuuji@0: { yuuji@0: char *s; yuuji@0: struct passwd *pw; yuuji@0: struct passwd *ret = NIL; yuuji@4: #ifndef QMAIL /* imapext md5 checker run previously. no need to do here */ yuuji@0: if (auth_md5.server) { /* using CRAM-MD5 authentication? */ yuuji@0: if (s = auth_md5_pwd (user)) { yuuji@0: if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1))) yuuji@0: ret = pwuser (user); /* validated, get passwd entry for user */ yuuji@0: memset (s,0,strlen (s)); /* erase sensitive information */ yuuji@0: fs_give ((void **) &s); yuuji@0: } yuuji@0: } yuuji@4: else yuuji@4: #endif yuuji@4: if (pw = pwuser (user)) {/* can get user? */ yuuji@0: s = cpystr (pw->pw_name); /* copy returned name in case we need it */ yuuji@0: if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) && yuuji@0: (*pwd == ' ') && pwd[1] && (ret = pwuser (s))) yuuji@0: ret = checkpw (pw,pwd+1,argc,argv); yuuji@0: fs_give ((void **) &s); /* don't need copy of name any more */ yuuji@0: } yuuji@0: return ret; yuuji@0: } yuuji@0: yuuji@0: /* Server log in yuuji@0: * Accepts: user name string yuuji@0: * password string yuuji@0: * authenticating user name string yuuji@0: * argument count yuuji@0: * argument vector yuuji@0: * Returns: T if password validated, NIL otherwise yuuji@0: */ yuuji@0: yuuji@0: long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[]) yuuji@0: { yuuji@0: struct passwd *pw = NIL; yuuji@0: int level = LOG_NOTICE; yuuji@0: char *err = "failed"; yuuji@4: #ifdef QMAIL yuuji@4: char usr[MAILTMPLEN], *apoppswd; yuuji@4: strncpy(usr, user, MAILTMPLEN-1); yuuji@4: #endif yuuji@0: /* cretins still haven't given up */ yuuji@0: if ((strlen (user) >= NETMAXUSER) || yuuji@0: (authuser && (strlen (authuser) >= NETMAXUSER))) { yuuji@0: level = LOG_ALERT; /* escalate this alert */ yuuji@0: err = "SYSTEM BREAK-IN ATTEMPT"; yuuji@0: logtry = 0; /* render this session useless */ yuuji@0: } yuuji@0: else if (logtry-- <= 0) err = "excessive login failures"; yuuji@0: else if (disablePlaintext) err = "disabled"; yuuji@4: #ifdef QMAIL yuuji@4: else if ((logtry > 0) && yuuji@4: (apoppswd = auth_md5_pwd(usr)) yuuji@4: && !strcmp(apoppswd, pwd) yuuji@4: && (pw = getpwnam(usr))) { yuuji@4: memset(apoppswd, 0, strlen(apoppswd)); yuuji@4: fs_give((void**) &apoppswd); yuuji@4: return pw_login(pw, usr, pw->pw_name, pw->pw_dir, argc, argv); yuuji@4: } yuuji@4: #endif yuuji@0: else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv); yuuji@0: else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user); yuuji@0: if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T; yuuji@0: syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err, yuuji@0: user,(authuser && *authuser) ? authuser : user,tcp_clienthost ()); yuuji@0: sleep (3); /* slow down possible cracker */ yuuji@0: return NIL; yuuji@0: } yuuji@0: yuuji@0: /* Authenticated server log in yuuji@0: * Accepts: user name string yuuji@0: * authenticating user name string yuuji@0: * argument count yuuji@0: * argument vector yuuji@0: * Returns: T if password validated, NIL otherwise yuuji@0: */ yuuji@0: yuuji@0: long authserver_login (char *user,char *authuser,int argc,char *argv[]) yuuji@0: { yuuji@0: return pw_login (pwuser (user),authuser,user,NIL,argc,argv); yuuji@0: } yuuji@0: yuuji@4: void permitsmtp() /* to update tcp permission */ yuuji@4: { yuuji@4: #ifdef POPBEFORESMTP yuuji@4: #include yuuji@4: #include yuuji@4: #ifndef POP3RECORDER yuuji@4: # define POP3RECORDER "/usr/local/etc/pop3-record" yuuji@4: #endif yuuji@4: int child; yuuji@4: int wstat; yuuji@4: char *permsmtp = POP3RECORDER; yuuji@4: yuuji@4: switch(child = fork()) yuuji@4: { yuuji@4: case -1: yuuji@4: syslog (LOG_INFO,"Cannot exec %s", permsmtp); yuuji@4: _exit(111); yuuji@4: break; yuuji@4: case 0: yuuji@4: execl(permsmtp, permsmtp, 0); yuuji@4: syslog (LOG_INFO,"Cannot exec %s", permsmtp); yuuji@4: _exit(111); break; yuuji@4: } yuuji@4: waitpid(child, &wstat, 0); yuuji@4: #endif yuuji@4: } yuuji@4: yuuji@4: yuuji@0: yuuji@0: /* Log in as anonymous daemon yuuji@0: * Accepts: argument count yuuji@0: * argument vector yuuji@0: * Returns: T if successful, NIL if error yuuji@0: */ yuuji@0: yuuji@0: long anonymous_login (int argc,char *argv[]) yuuji@0: { yuuji@0: /* log in Mr. A. N. Onymous */ yuuji@0: return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL, yuuji@0: (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL), yuuji@0: argc,argv); yuuji@0: } yuuji@0: yuuji@0: /* Finish log in and environment initialization yuuji@0: * Accepts: passwd struct for loginpw() yuuji@0: * optional authentication user name yuuji@0: * user name (NIL for anonymous) yuuji@0: * home directory (NIL to use directory from passwd struct) yuuji@0: * argument count yuuji@0: * argument vector yuuji@0: * Returns: T if successful, NIL if error yuuji@0: */ yuuji@0: yuuji@0: long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc, yuuji@0: char *argv[]) yuuji@0: { yuuji@0: struct group *gr; yuuji@0: char **t; yuuji@0: long ret = NIL; yuuji@0: if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */ yuuji@0: /* make safe copies of user and home */ yuuji@0: if (user) user = cpystr (pw->pw_name); yuuji@0: home = cpystr (home ? home : pw->pw_dir); yuuji@0: /* authorization ID .NE. authentication ID? */ yuuji@0: if (user && auser && *auser && compare_cstring (auser,user)) { yuuji@0: /* scan list of mail administrators */ yuuji@0: if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret) yuuji@0: if (!compare_cstring (auser,*t++)) yuuji@0: ret = pw_login (pw,NIL,user,home,argc,argv); yuuji@0: syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s", yuuji@0: ret ? "Admin" : "Failed",auser,user,tcp_clienthost ()); yuuji@0: } yuuji@0: else if (closedBox) { /* paranoid site, lock out other directories */ yuuji@0: if (chdir (home) || chroot (home)) yuuji@0: syslog (LOG_NOTICE|LOG_AUTH, yuuji@0: "Login %s failed: unable to set chroot=%.80s host=%.80s", yuuji@0: pw->pw_name,home,tcp_clienthost ()); yuuji@0: else if (loginpw (pw,argc,argv)) ret = env_init (user,NIL); yuuji@0: else fatal ("Login failed after chroot"); yuuji@0: } yuuji@0: /* normal login */ yuuji@4: #ifdef QMAIL yuuji@4: else if (((pw->pw_uid == geteuid ()) || (permitsmtp(), loginpw (pw,argc,argv yuuji@4: ))) && yuuji@4: (ret = env_init (user,home))) chdir (myhomedir ()); yuuji@4: #else yuuji@0: else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) && yuuji@4: (ret = env_init (user,home))) chdir (myhomedir ()); yuuji@4: #endif yuuji@0: fs_give ((void **) &home); /* clean up */ yuuji@0: if (user) fs_give ((void **) &user); yuuji@0: } yuuji@0: endpwent (); /* in case shadow passwords in pw data */ yuuji@0: return ret; /* return status */ yuuji@0: } yuuji@0: yuuji@0: /* Initialize environment yuuji@0: * Accepts: user name (NIL for anonymous) yuuji@0: * home directory name yuuji@0: * Returns: T, always yuuji@0: */ yuuji@0: yuuji@0: long env_init (char *user,char *home) yuuji@0: { yuuji@0: extern MAILSTREAM CREATEPROTO; yuuji@0: extern MAILSTREAM EMPTYPROTO; yuuji@0: struct passwd *pw; yuuji@0: struct stat sbuf; yuuji@0: char tmp[MAILTMPLEN]; yuuji@0: /* don't init if blocked */ yuuji@0: if (block_env_init) return LONGT; yuuji@0: if (myUserName) fatal ("env_init called twice!"); yuuji@0: /* initially nothing in namespace list */ yuuji@0: nslist[0] = nslist[1] = nslist[2] = NIL; yuuji@0: /* myUserName must be set before dorc() call */ yuuji@0: myUserName = cpystr (user ? user : ANONYMOUSUSER); yuuji@0: /* force default prototypes to be set */ yuuji@0: if (!createProto) createProto = &CREATEPROTO; yuuji@0: if (!appendProto) appendProto = &EMPTYPROTO; yuuji@0: dorc (NIL,NIL); /* do systemwide configuration */ yuuji@0: if (!home) { /* closed box server */ yuuji@0: /* standard user can only reference home */ yuuji@0: if (user) nslist[0] = &nshome; yuuji@0: else { /* anonymous user */ yuuji@0: nslist[0] = &nsblackother; /* set root */ yuuji@0: anonymous = T; /* flag as anonymous */ yuuji@0: } yuuji@0: myHomeDir = cpystr (""); /* home directory is root */ yuuji@0: sysInbox = cpystr ("INBOX");/* make system INBOX */ yuuji@0: } yuuji@0: else { /* open or black box */ yuuji@0: closedBox = NIL; /* definitely not a closed box */ yuuji@0: if (user) { /* remember user name and home directory */ yuuji@0: if (blackBoxDir) { /* build black box directory name */ yuuji@0: sprintf (tmp,"%s/%s",blackBoxDir,myUserName); yuuji@0: /* must exist */ yuuji@0: if (!((!stat (home = tmp,&sbuf) && (sbuf.st_mode & S_IFDIR)) || yuuji@0: (blackBoxDefaultHome && yuuji@0: !stat (home = blackBoxDefaultHome,&sbuf) && yuuji@0: (sbuf.st_mode & S_IFDIR)))) fatal ("no home"); yuuji@0: sysInbox = (char *) fs_get (strlen (home) + 7); yuuji@0: /* set system INBOX */ yuuji@0: sprintf (sysInbox,"%s/INBOX",home); yuuji@0: blackBox = T; /* mark that it's a black box */ yuuji@0: /* mbox meaningless if black box */ yuuji@0: mail_parameters (NIL,DISABLE_DRIVER,(void *) "mbox"); yuuji@0: } yuuji@0: nslist[0] = &nshome; /* home namespace */ yuuji@0: /* limited advertise namespaces */ yuuji@0: if (limitedadvertise) nslist[2] = &nslimited; yuuji@0: else if (blackBox) { /* black box namespaces */ yuuji@0: nslist[1] = &nsblackother; yuuji@0: nslist[2] = &nsshared; yuuji@0: } yuuji@0: else { /* open box namespaces */ yuuji@0: nslist[1] = &nsunixother; yuuji@0: nslist[2] = advertisetheworld ? &nsworld : &nsshared; yuuji@0: } yuuji@0: } yuuji@0: else { yuuji@0: nslist[2] = &nsftp; /* anonymous user */ yuuji@0: sprintf (tmp,"%s/INBOX", yuuji@0: home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL)); yuuji@0: sysInbox = cpystr (tmp); /* make system INBOX */ yuuji@0: anonymous = T; /* flag as anonymous */ yuuji@0: } yuuji@0: myHomeDir = cpystr (home); /* set home directory */ yuuji@0: } yuuji@0: yuuji@0: if (allowuserconfig) { /* allow user config files */ yuuji@0: dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T); yuuji@0: dorc (strcat (strcpy (tmp,myHomeDir),"/.imaprc"),NIL); yuuji@0: } yuuji@0: if (!closedBox && !noautomaticsharedns) { yuuji@0: /* #ftp namespace */ yuuji@0: if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir); yuuji@0: /* #public namespace */ yuuji@0: if (!publicHome && (pw = getpwnam ("imappublic"))) yuuji@0: publicHome = cpystr (pw->pw_dir); yuuji@0: /* #shared namespace */ yuuji@0: if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared"))) yuuji@0: sharedHome = cpystr (pw->pw_dir); yuuji@0: } yuuji@0: if (!myLocalHost) mylocalhost (); yuuji@0: if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc")); yuuji@0: if (!newsActive) newsActive = cpystr (ACTIVEFILE); yuuji@0: if (!newsSpool) newsSpool = cpystr (NEWSSPOOL); yuuji@0: /* re-do open action to get flags */ yuuji@0: (*createProto->dtb->open) (NIL); yuuji@0: endpwent (); /* close pw database */ yuuji@0: return T; yuuji@0: } yuuji@0: yuuji@0: /* Return my user name yuuji@0: * Accepts: pointer to optional flags yuuji@0: * Returns: my user name yuuji@0: */ yuuji@0: yuuji@0: char *myusername_full (unsigned long *flags) yuuji@0: { yuuji@0: struct passwd *pw; yuuji@0: struct stat sbuf; yuuji@0: char *s; yuuji@0: unsigned long euid; yuuji@0: char *ret = UNLOGGEDUSER; yuuji@0: /* no user name yet and not root? */ yuuji@0: if (!myUserName && (euid = geteuid ())) { yuuji@0: /* yes, look up getlogin() user name or EUID */ yuuji@0: if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) && yuuji@0: (pw = getpwnam (s)) && (pw->pw_uid == euid)) || yuuji@0: (pw = getpwuid (euid))) { yuuji@0: if (block_env_init) { /* don't env_init if blocked */ yuuji@0: if (flags) *flags = MU_LOGGEDIN; yuuji@0: return pw->pw_name; yuuji@0: } yuuji@0: env_init (pw->pw_name, yuuji@0: ((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) && yuuji@0: !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ? yuuji@0: s : pw->pw_dir); yuuji@0: } yuuji@0: else fatal ("Unable to look up user name"); yuuji@0: } yuuji@0: if (myUserName) { /* logged in? */ yuuji@0: if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN; yuuji@0: ret = myUserName; /* return user name */ yuuji@0: } yuuji@0: else if (flags) *flags = MU_NOTLOGGEDIN; yuuji@0: return ret; yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Return my local host name yuuji@0: * Returns: my local host name yuuji@0: */ yuuji@0: yuuji@0: char *mylocalhost () yuuji@0: { yuuji@0: if (!myLocalHost) { yuuji@0: char *s,tmp[MAILTMPLEN]; yuuji@0: char *t = "unknown"; yuuji@0: tmp[0] = tmp[MAILTMPLEN-1] = '\0'; yuuji@0: if (!gethostname (tmp,MAILTMPLEN-1) && tmp[0]) { yuuji@0: /* sanity check of name */ yuuji@0: for (s = tmp; (*s > 0x20) && (*s < 0x7f); ++s); yuuji@0: if (!*s) t = tcp_canonical (tmp); yuuji@0: } yuuji@0: myLocalHost = cpystr (t); yuuji@0: } yuuji@0: return myLocalHost; yuuji@0: } yuuji@0: yuuji@0: /* Return my home directory name yuuji@0: * Returns: my home directory name yuuji@0: */ yuuji@0: yuuji@0: char *myhomedir () yuuji@0: { yuuji@0: if (!myHomeDir) myusername ();/* initialize if first time */ yuuji@0: return myHomeDir ? myHomeDir : ""; yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Return my home mailbox name yuuji@0: * Returns: my home directory name yuuji@0: */ yuuji@0: yuuji@0: static char *mymailboxdir () yuuji@0: { yuuji@0: char *home = myhomedir (); yuuji@0: /* initialize if first time */ yuuji@0: if (!myMailboxDir && myHomeDir) { yuuji@0: if (mailsubdir) { yuuji@0: char tmp[MAILTMPLEN]; yuuji@0: sprintf (tmp,"%s/%s",home,mailsubdir); yuuji@0: myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */ yuuji@0: } yuuji@0: else myMailboxDir = cpystr (home); yuuji@0: } yuuji@0: return myMailboxDir ? myMailboxDir : ""; yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Return system standard INBOX yuuji@0: * Accepts: buffer string yuuji@0: */ yuuji@0: yuuji@0: char *sysinbox () yuuji@0: { yuuji@0: char tmp[MAILTMPLEN]; yuuji@0: if (!sysInbox) { /* initialize if first time */ yuuji@0: sprintf (tmp,"%s/%s",MAILSPOOL,myusername ()); yuuji@0: sysInbox = cpystr (tmp); /* system inbox is from mail spool */ yuuji@0: } yuuji@0: return sysInbox; yuuji@0: } yuuji@0: yuuji@0: /* Return mailbox directory name yuuji@0: * Accepts: destination buffer yuuji@0: * directory prefix yuuji@0: * name in directory yuuji@0: * Returns: file name or NIL if error yuuji@0: */ yuuji@0: yuuji@0: char *mailboxdir (char *dst,char *dir,char *name) yuuji@0: { yuuji@0: char tmp[MAILTMPLEN]; yuuji@0: if (dir || name) { /* if either argument provided */ yuuji@0: if (dir) { yuuji@0: if (strlen (dir) > NETMAXMBX) return NIL; yuuji@0: strcpy (tmp,dir); /* write directory prefix */ yuuji@0: } yuuji@0: else tmp[0] = '\0'; /* otherwise null string */ yuuji@0: if (name) { yuuji@0: if (strlen (name) > NETMAXMBX) return NIL; yuuji@0: strcat (tmp,name); /* write name in directory */ yuuji@0: } yuuji@0: /* validate name, return its name */ yuuji@0: if (!mailboxfile (dst,tmp)) return NIL; yuuji@0: } yuuji@0: /* no arguments, wants mailbox directory */ yuuji@0: else strcpy (dst,mymailboxdir ()); yuuji@0: return dst; /* return the name */ yuuji@0: } yuuji@0: yuuji@0: /* Return mailbox file name yuuji@0: * Accepts: destination buffer yuuji@0: * mailbox name yuuji@0: * Returns: file name or empty string for driver-selected INBOX or NIL if error yuuji@0: */ yuuji@0: yuuji@0: char *mailboxfile (char *dst,char *name) yuuji@0: { yuuji@0: struct passwd *pw; yuuji@0: char *s; yuuji@0: if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) || yuuji@0: ((anonymous || blackBox || restrictBox || (*name == '#')) && yuuji@0: (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~")))) yuuji@0: dst = NIL; /* invalid name */ yuuji@0: else switch (*name) { /* determine mailbox type based upon name */ yuuji@0: case '#': /* namespace name */ yuuji@0: /* #ftp/ namespace */ yuuji@0: if (((name[1] == 'f') || (name[1] == 'F')) && yuuji@0: ((name[2] == 't') || (name[2] == 'T')) && yuuji@0: ((name[3] == 'p') || (name[3] == 'P')) && yuuji@0: (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5); yuuji@0: /* #public/ and #shared/ namespaces */ yuuji@0: else if ((((name[1] == 'p') || (name[1] == 'P')) && yuuji@0: ((name[2] == 'u') || (name[2] == 'U')) && yuuji@0: ((name[3] == 'b') || (name[3] == 'B')) && yuuji@0: ((name[4] == 'l') || (name[4] == 'L')) && yuuji@0: ((name[5] == 'i') || (name[5] == 'I')) && yuuji@0: ((name[6] == 'c') || (name[6] == 'C')) && yuuji@0: (name[7] == '/') && (s = publicHome)) || yuuji@0: (!anonymous && ((name[1] == 's') || (name[1] == 'S')) && yuuji@0: ((name[2] == 'h') || (name[2] == 'H')) && yuuji@0: ((name[3] == 'a') || (name[3] == 'A')) && yuuji@0: ((name[4] == 'r') || (name[4] == 'R')) && yuuji@0: ((name[5] == 'e') || (name[5] == 'E')) && yuuji@0: ((name[6] == 'd') || (name[6] == 'D')) && yuuji@0: (name[7] == '/') && (s = sharedHome))) yuuji@0: sprintf (dst,"%s/%s",s,compare_cstring (name+8,"INBOX") ? yuuji@0: name+8 : "INBOX"); yuuji@0: else dst = NIL; /* unknown namespace */ yuuji@0: break; yuuji@0: yuuji@0: case '/': /* root access */ yuuji@0: if (anonymous) dst = NIL; /* anonymous forbidden to do this */ yuuji@0: else if (blackBox) { /* other user access if blackbox */ yuuji@0: if (restrictBox & RESTRICTOTHERUSER) dst = NIL; yuuji@0: /* see if other user INBOX */ yuuji@0: else if ((s = strchr (name+1,'/')) && !compare_cstring (s+1,"INBOX")) { yuuji@0: *s = '\0'; /* temporarily tie off string */ yuuji@0: sprintf (dst,"%s/%s/INBOX",blackBoxDir,name+1); yuuji@0: *s = '/'; /* in case caller cares */ yuuji@0: } yuuji@0: else sprintf (dst,"%s/%s",blackBoxDir,name+1); yuuji@0: } yuuji@0: else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ())) yuuji@0: dst = NIL; /* restricted and not access to sysinbox */ yuuji@0: else strcpy (dst,name); /* unrestricted, copy root name */ yuuji@0: break; yuuji@0: case '~': /* other user access */ yuuji@0: /* bad syntax or anonymous can't win */ yuuji@0: if (!*++name || anonymous) dst = NIL; yuuji@0: /* ~/ equivalent to ordinary name */ yuuji@0: else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1); yuuji@0: /* other user forbidden if closed/restricted */ yuuji@0: else if (closedBox || (restrictBox & RESTRICTOTHERUSER)) dst = NIL; yuuji@0: else if (blackBox) { /* black box form of other user */ yuuji@0: /* see if other user INBOX */ yuuji@0: if ((s = strchr (name,'/')) && compare_cstring (s+1,"INBOX")) { yuuji@0: *s = '\0'; /* temporarily tie off string */ yuuji@0: sprintf (dst,"%s/%s/INBOX",blackBoxDir,name); yuuji@0: *s = '/'; /* in case caller cares */ yuuji@0: } yuuji@0: else sprintf (dst,"%s/%s",blackBoxDir,name); yuuji@0: } yuuji@0: else { /* clear box other user */ yuuji@0: /* copy user name */ yuuji@0: for (s = dst; *name && (*name != '/'); *s++ = *name++); yuuji@0: *s++ = '\0'; /* tie off user name, look up in passwd file */ yuuji@0: if ((pw = getpwnam (dst)) && pw->pw_dir) { yuuji@0: if (*name) name++; /* skip past the slash */ yuuji@0: /* canonicalize case of INBOX */ yuuji@0: if (!compare_cstring (name,"INBOX")) name = "INBOX"; yuuji@0: /* remove trailing / from directory */ yuuji@0: if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0'; yuuji@0: /* don't allow ~root/ if restricted root */ yuuji@0: if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL; yuuji@0: /* build final name w/ subdir if needed */ yuuji@0: else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name); yuuji@0: else sprintf (dst,"%s/%s",pw->pw_dir,name); yuuji@0: } yuuji@0: else dst = NIL; /* no such user */ yuuji@0: } yuuji@0: break; yuuji@0: yuuji@0: case 'I': case 'i': /* possible INBOX */ yuuji@0: if (!compare_cstring (name+1,"NBOX")) { yuuji@0: /* if restricted, use INBOX in mailbox dir */ yuuji@0: if (anonymous || blackBox || closedBox) yuuji@0: sprintf (dst,"%s/INBOX",mymailboxdir ()); yuuji@0: else *dst = '\0'; /* otherwise driver selects the name */ yuuji@0: break; yuuji@0: } yuuji@0: /* drop into to ordinary name case */ yuuji@0: default: /* ordinary name is easy */ yuuji@0: sprintf (dst,"%s/%s",mymailboxdir (),name); yuuji@0: break; yuuji@0: } yuuji@0: return dst; /* return final name */ yuuji@0: } yuuji@0: yuuji@0: /* Dot-lock file locker yuuji@0: * Accepts: file name to lock yuuji@0: * destination buffer for lock file name yuuji@0: * open file description on file name to lock yuuji@0: * Returns: T if success, NIL if failure yuuji@0: */ yuuji@0: yuuji@0: long dotlock_lock (char *file,DOTLOCK *base,int fd) yuuji@0: { yuuji@0: int i = locktimeout * 60; yuuji@0: int j,mask,retry,pi[2],po[2]; yuuji@0: char *s,tmp[MAILTMPLEN]; yuuji@0: struct stat sb; yuuji@0: /* flush absurd file name */ yuuji@0: if (strlen (file) > 512) return NIL; yuuji@0: /* build lock filename */ yuuji@0: sprintf (base->lock,"%s.lock",file); yuuji@0: /* assume no pipe */ yuuji@0: base->pipei = base->pipeo = -1; yuuji@0: do { /* make sure not symlink */ yuuji@0: if (!(j = chk_notsymlink (base->lock,&sb))) return NIL; yuuji@0: /* time out if file older than 5 minutes */ yuuji@0: if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0; yuuji@0: /* try to create the lock */ yuuji@0: switch (retry = crexcl (base->lock)) { yuuji@0: case -1: /* OK to retry */ yuuji@0: if (!(i%15)) { /* time to notify? */ yuuji@0: sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...", yuuji@0: file,i); yuuji@0: MM_LOG (tmp,WARN); yuuji@0: } yuuji@0: sleep (1); /* wait 1 second before next try */ yuuji@0: break; yuuji@0: case NIL: /* failure, can't retry */ yuuji@0: i = 0; yuuji@0: break; yuuji@0: case T: /* success, make sure others can break lock */ yuuji@0: chmod (base->lock,(int) dotlock_mode); yuuji@0: return LONGT; yuuji@0: } yuuji@0: } while (i--); /* until out of retries */ yuuji@0: if (retry < 0) { /* still returning retry after locktimeout? */ yuuji@0: if (!(j = chk_notsymlink (base->lock,&sb))) return NIL; yuuji@0: if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) { yuuji@0: sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock", yuuji@0: (long) (time (0) - sb.st_ctime)); yuuji@0: MM_LOG (tmp,WARN); yuuji@0: } yuuji@0: mask = umask (0); /* want our lock protection */ yuuji@0: unlink (base->lock); /* try to remove the old file */ yuuji@0: /* seize the lock */ yuuji@0: if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) dotlock_mode)) >= 0) { yuuji@0: close (i); /* don't need descriptor any more */ yuuji@0: sprintf (tmp,"Mailbox %.80s lock overridden",file); yuuji@0: MM_LOG (tmp,NIL); yuuji@0: chmod (base->lock,(int) dotlock_mode); yuuji@0: umask (mask); /* restore old umask */ yuuji@0: return LONGT; yuuji@0: } yuuji@0: umask (mask); /* restore old umask */ yuuji@0: } yuuji@0: yuuji@0: if (fd >= 0) switch (errno) { yuuji@0: case EACCES: /* protection failure? */ yuuji@0: MM_CRITICAL (NIL); /* go critical */ yuuji@0: if (closedBox || !lockpgm); /* can't do on closed box or disabled */ yuuji@0: else if ((*lockpgm && stat (lockpgm,&sb)) || yuuji@0: (!*lockpgm && stat (lockpgm = LOCKPGM1,&sb) && yuuji@0: stat (lockpgm = LOCKPGM2,&sb) && stat (lockpgm = LOCKPGM3,&sb))) yuuji@0: lockpgm = NIL; /* disable if can't find lockpgm */ yuuji@0: else if (pipe (pi) >= 0) { /* make command pipes */ yuuji@0: long cf; yuuji@0: char *argv[4],arg[20]; yuuji@0: /* if input pipes usable create output pipes */ yuuji@0: if ((pi[0] < FD_SETSIZE) && (pi[1] < FD_SETSIZE) && (pipe (po) >= 0)) { yuuji@0: /* make sure output pipes are usable */ yuuji@0: if ((po[0] >= FD_SETSIZE) || (po[1] >= FD_SETSIZE)); yuuji@0: /* all is good, make inferior process */ yuuji@0: else if (!(j = fork ())) { yuuji@0: if (!fork ()) { /* make grandchild so it's inherited by init */ yuuji@0: /* prepare argument vector */ yuuji@0: sprintf (arg,"%d",fd); yuuji@0: argv[0] = lockpgm; argv[1] = arg; yuuji@0: argv[2] = file; argv[3] = NIL; yuuji@0: /* set parent's I/O to my O/I */ yuuji@0: dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0); yuuji@0: /* close all unnecessary descriptors */ yuuji@0: for (cf = max (20,max (max (pi[0],pi[1]),max(po[0],po[1]))); yuuji@0: cf >= 3; --cf) if (cf != fd) close (cf); yuuji@0: /* be our own process group */ yuuji@0: setpgrp (0,getpid ()); yuuji@0: /* now run it */ yuuji@0: _exit (execv (argv[0],argv)); yuuji@0: } yuuji@0: _exit (1); /* child is done */ yuuji@0: } yuuji@0: else if (j > 0) { /* parent process */ yuuji@0: fd_set rfd; yuuji@0: struct timeval tmo; yuuji@0: FD_ZERO (&rfd); yuuji@0: FD_SET (pi[0],&rfd); yuuji@0: tmo.tv_sec = locktimeout * 60; yuuji@0: grim_pid_reap (j,NIL);/* reap child; grandchild now owned by init */ yuuji@0: /* read response from locking program */ yuuji@0: if (select (pi[0]+1,&rfd,0,0,&tmo) && yuuji@0: (read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) { yuuji@0: /* success, record pipes */ yuuji@0: base->pipei = pi[0]; base->pipeo = po[1]; yuuji@0: /* close child's side of the pipes */ yuuji@0: close (pi[1]); close (po[0]); yuuji@0: MM_NOCRITICAL (NIL);/* no longer critical */ yuuji@0: return LONGT; yuuji@0: } yuuji@0: } yuuji@0: close (po[0]); close (po[1]); yuuji@0: } yuuji@0: close (pi[0]); close (pi[1]); yuuji@0: } yuuji@0: yuuji@0: MM_NOCRITICAL (NIL); /* no longer critical */ yuuji@0: /* find directory/file delimiter */ yuuji@0: if (s = strrchr (base->lock,'/')) { yuuji@0: *s = '\0'; /* tie off at directory */ yuuji@0: sprintf(tmp, /* generate default message */ yuuji@0: "Mailbox vulnerable - directory %.80s must have 1777 protection", yuuji@0: base->lock); yuuji@0: /* definitely not 1777 if can't stat */ yuuji@0: mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777); yuuji@0: *s = '/'; /* restore lock name */ yuuji@0: if (mask != 1777) { /* default warning if not 1777 */ yuuji@0: if (!disableLockWarning) MM_LOG (tmp,WARN); yuuji@0: break; yuuji@0: } yuuji@0: } yuuji@0: default: yuuji@0: sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s", yuuji@0: base->lock,strerror (errno)); yuuji@0: if (!disableLockWarning) MM_LOG (tmp,WARN); yuuji@0: break; yuuji@0: } yuuji@0: base->lock[0] = '\0'; /* don't use lock files */ yuuji@0: return NIL; yuuji@0: } yuuji@0: yuuji@0: /* Dot-lock file unlocker yuuji@0: * Accepts: lock file name yuuji@0: * Returns: T if success, NIL if failure yuuji@0: */ yuuji@0: yuuji@0: long dotlock_unlock (DOTLOCK *base) yuuji@0: { yuuji@0: long ret = LONGT; yuuji@0: if (base && base->lock[0]) { yuuji@0: if (base->pipei >= 0) { /* if running through a pipe unlocker */ yuuji@0: ret = (write (base->pipeo,"+",1) == 1); yuuji@0: /* nuke the pipes */ yuuji@0: close (base->pipei); close (base->pipeo); yuuji@0: } yuuji@0: else ret = !unlink (base->lock); yuuji@0: } yuuji@0: return ret; yuuji@0: } yuuji@0: yuuji@0: /* Lock file name yuuji@0: * Accepts: scratch buffer yuuji@0: * file name yuuji@0: * type of locking operation (LOCK_SH or LOCK_EX) yuuji@0: * pointer to return PID of locker yuuji@0: * Returns: file descriptor of lock or negative if error yuuji@0: */ yuuji@0: yuuji@0: int lockname (char *lock,char *fname,int op,long *pid) yuuji@0: { yuuji@0: struct stat sbuf; yuuji@0: *pid = 0; /* no locker PID */ yuuji@0: return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid); yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Lock file descriptor yuuji@0: * Accepts: file descriptor yuuji@0: * lock file name buffer yuuji@0: * type of locking operation (LOCK_SH or LOCK_EX) yuuji@0: * Returns: file descriptor of lock or negative if error yuuji@0: */ yuuji@0: yuuji@0: int lockfd (int fd,char *lock,int op) yuuji@0: { yuuji@0: struct stat sbuf; yuuji@0: return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL); yuuji@0: } yuuji@0: yuuji@0: /* Lock file name worker yuuji@0: * Accepts: lock file name yuuji@0: * pointer to stat() buffer yuuji@0: * type of locking operation (LOCK_SH or LOCK_EX) yuuji@0: * pointer to return PID of locker yuuji@0: * Returns: file descriptor of lock or negative if error yuuji@0: */ yuuji@0: yuuji@0: int lock_work (char *lock,void *sb,int op,long *pid) yuuji@0: { yuuji@0: struct stat lsb,fsb; yuuji@0: struct stat *sbuf = (struct stat *) sb; yuuji@0: char tmp[MAILTMPLEN]; yuuji@0: long i; yuuji@0: int fd; yuuji@0: int mask = umask (0); yuuji@0: if (pid) *pid = 0; /* initialize return PID */ yuuji@0: /* make temporary lock file name */ yuuji@0: sprintf (lock,"%s/.%lx.%lx",closedBox ? "" : tmpdir, yuuji@0: (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino); yuuji@0: while (T) { /* until get a good lock */ yuuji@0: do switch ((int) chk_notsymlink (lock,&lsb)) { yuuji@0: case 1: /* exists just once */ yuuji@0: if (((fd = open (lock,O_RDWR,shlock_mode)) >= 0) || yuuji@0: (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break; yuuji@0: case -1: /* name doesn't exist */ yuuji@0: fd = open (lock,O_RDWR|O_CREAT|O_EXCL,shlock_mode); yuuji@0: break; yuuji@0: default: /* multiple hard links */ yuuji@0: MM_LOG ("hard link to lock name",ERROR); yuuji@0: syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock); yuuji@0: case 0: /* symlink (already did syslog) */ yuuji@0: umask (mask); /* restore old mask */ yuuji@0: return -1; /* fail: no lock file */ yuuji@0: } while ((fd < 0) && (errno == EEXIST)); yuuji@0: if (fd < 0) { /* failed to get file descriptor */ yuuji@0: syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock, yuuji@0: strerror (errno)); yuuji@0: if (!closedBox) { /* more explicit snarl for bad configuration */ yuuji@0: if (stat (tmpdir,&lsb)) yuuji@0: syslog (LOG_CRIT,"SYSTEM ERROR: no %s: %s",tmpdir,strerror (errno)); yuuji@0: else if ((lsb.st_mode & 01777) != 01777) { yuuji@0: sprintf (tmp,"Can't lock for write: %.80s must have 1777 protection", yuuji@0: tmpdir); yuuji@0: MM_LOG (tmp,WARN); yuuji@0: } yuuji@0: } yuuji@0: umask (mask); /* restore old mask */ yuuji@0: return -1; /* fail: can't open lock file */ yuuji@0: } yuuji@0: yuuji@0: /* non-blocking form */ yuuji@0: if (op & LOCK_NB) i = flock (fd,op); yuuji@0: else { /* blocking form */ yuuji@0: (*mailblocknotify) (BLOCK_FILELOCK,NIL); yuuji@0: i = flock (fd,op); yuuji@0: (*mailblocknotify) (BLOCK_NONE,NIL); yuuji@0: } yuuji@0: if (i) { /* failed, get other process' PID */ yuuji@0: if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) && yuuji@0: (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0)) yuuji@0: *pid = i; yuuji@0: close (fd); /* failed, give up on lock */ yuuji@0: umask (mask); /* restore old mask */ yuuji@0: return -1; /* fail: can't lock */ yuuji@0: } yuuji@0: /* make sure this lock is good for us */ yuuji@0: if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) && yuuji@0: !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) && yuuji@0: (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break; yuuji@0: close (fd); /* lock not right, drop fd and try again */ yuuji@0: } yuuji@0: chmod (lock,shlock_mode); /* make sure mode OK (don't use fchmod()) */ yuuji@0: umask (mask); /* restore old mask */ yuuji@0: return fd; /* success */ yuuji@0: } yuuji@0: yuuji@0: /* Check to make sure not a symlink yuuji@0: * Accepts: file name yuuji@0: * stat buffer yuuji@0: * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links yuuji@0: */ yuuji@0: yuuji@0: long chk_notsymlink (char *name,void *sb) yuuji@0: { yuuji@0: struct stat *sbuf = (struct stat *) sb; yuuji@0: /* name exists? */ yuuji@0: if (lstat (name,sbuf)) return -1; yuuji@0: /* forbid symbolic link */ yuuji@0: if ((sbuf->st_mode & S_IFMT) == S_IFLNK) { yuuji@0: MM_LOG ("symbolic link on lock name",ERROR); yuuji@0: syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s", yuuji@0: name); yuuji@0: return NIL; yuuji@0: } yuuji@0: return (long) sbuf->st_nlink; /* return number of hard links */ yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Unlock file descriptor yuuji@0: * Accepts: file descriptor yuuji@0: * lock file name from lockfd() yuuji@0: */ yuuji@0: yuuji@0: void unlockfd (int fd,char *lock) yuuji@0: { yuuji@0: /* delete the file if no sharers */ yuuji@0: if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock); yuuji@0: flock (fd,LOCK_UN); /* unlock it */ yuuji@0: close (fd); /* close it */ yuuji@0: } yuuji@0: yuuji@0: /* Set proper file protection for mailbox yuuji@0: * Accepts: mailbox name yuuji@0: * actual file path name yuuji@0: * Returns: T, always yuuji@0: */ yuuji@0: yuuji@0: long set_mbx_protections (char *mailbox,char *path) yuuji@0: { yuuji@0: struct stat sbuf; yuuji@0: int mode = (int) mbx_protection; yuuji@0: if (*mailbox == '#') { /* possible namespace? */ yuuji@0: if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) && yuuji@0: ((mailbox[2] == 't') || (mailbox[2] == 'T')) && yuuji@0: ((mailbox[3] == 'p') || (mailbox[3] == 'P')) && yuuji@0: (mailbox[4] == '/')) mode = (int) ftp_protection; yuuji@0: else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) && yuuji@0: ((mailbox[2] == 'u') || (mailbox[2] == 'U')) && yuuji@0: ((mailbox[3] == 'b') || (mailbox[3] == 'B')) && yuuji@0: ((mailbox[4] == 'l') || (mailbox[4] == 'L')) && yuuji@0: ((mailbox[5] == 'i') || (mailbox[5] == 'I')) && yuuji@0: ((mailbox[6] == 'c') || (mailbox[6] == 'C')) && yuuji@0: (mailbox[7] == '/')) mode = (int) public_protection; yuuji@0: else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) && yuuji@0: ((mailbox[2] == 'h') || (mailbox[2] == 'H')) && yuuji@0: ((mailbox[3] == 'a') || (mailbox[3] == 'A')) && yuuji@0: ((mailbox[4] == 'r') || (mailbox[4] == 'R')) && yuuji@0: ((mailbox[5] == 'e') || (mailbox[5] == 'E')) && yuuji@0: ((mailbox[6] == 'd') || (mailbox[6] == 'D')) && yuuji@0: (mailbox[7] == '/')) mode = (int) shared_protection; yuuji@0: } yuuji@0: /* if a directory */ yuuji@0: if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) { yuuji@0: /* set owner search if allow read or write */ yuuji@0: if (mode & 0600) mode |= 0100; yuuji@0: if (mode & 060) mode |= 010;/* set group search if allow read or write */ yuuji@0: if (mode & 06) mode |= 01; /* set world search if allow read or write */ yuuji@0: /* preserve directory SGID bit */ yuuji@0: if (sbuf.st_mode & S_ISGID) mode |= S_ISGID; yuuji@0: } yuuji@0: chmod (path,mode); /* set the new protection, ignore failure */ yuuji@0: return LONGT; yuuji@0: } yuuji@0: yuuji@0: /* Get proper directory protection yuuji@0: * Accepts: mailbox name yuuji@0: * Returns: directory mode, always yuuji@0: */ yuuji@0: yuuji@0: long get_dir_protection (char *mailbox) yuuji@0: { yuuji@0: if (*mailbox == '#') { /* possible namespace? */ yuuji@0: if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) && yuuji@0: ((mailbox[2] == 't') || (mailbox[2] == 'T')) && yuuji@0: ((mailbox[3] == 'p') || (mailbox[3] == 'P')) && yuuji@0: (mailbox[4] == '/')) return ftp_dir_protection; yuuji@0: else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) && yuuji@0: ((mailbox[2] == 'u') || (mailbox[2] == 'U')) && yuuji@0: ((mailbox[3] == 'b') || (mailbox[3] == 'B')) && yuuji@0: ((mailbox[4] == 'l') || (mailbox[4] == 'L')) && yuuji@0: ((mailbox[5] == 'i') || (mailbox[5] == 'I')) && yuuji@0: ((mailbox[6] == 'c') || (mailbox[6] == 'C')) && yuuji@0: (mailbox[7] == '/')) return public_dir_protection; yuuji@0: else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) && yuuji@0: ((mailbox[2] == 'h') || (mailbox[2] == 'H')) && yuuji@0: ((mailbox[3] == 'a') || (mailbox[3] == 'A')) && yuuji@0: ((mailbox[4] == 'r') || (mailbox[4] == 'R')) && yuuji@0: ((mailbox[5] == 'e') || (mailbox[5] == 'E')) && yuuji@0: ((mailbox[6] == 'd') || (mailbox[6] == 'D')) && yuuji@0: (mailbox[7] == '/')) return shared_dir_protection; yuuji@0: } yuuji@0: return dir_protection; yuuji@0: } yuuji@0: yuuji@0: /* Determine default prototype stream to user yuuji@0: * Accepts: type (NIL for create, T for append) yuuji@0: * Returns: default prototype stream yuuji@0: */ yuuji@0: yuuji@0: MAILSTREAM *default_proto (long type) yuuji@0: { yuuji@0: myusername (); /* make sure initialized */ yuuji@0: /* return default driver's prototype */ yuuji@0: return type ? appendProto : createProto; yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Set up user flags for stream yuuji@0: * Accepts: MAIL stream yuuji@0: * Returns: MAIL stream with user flags set up yuuji@0: */ yuuji@0: yuuji@0: MAILSTREAM *user_flags (MAILSTREAM *stream) yuuji@0: { yuuji@0: int i; yuuji@0: myusername (); /* make sure initialized */ yuuji@0: for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i) yuuji@0: if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]); yuuji@0: return stream; yuuji@0: } yuuji@0: yuuji@0: yuuji@0: /* Return nth user flag yuuji@0: * Accepts: user flag number yuuji@0: * Returns: flag yuuji@0: */ yuuji@0: yuuji@0: char *default_user_flag (unsigned long i) yuuji@0: { yuuji@0: myusername (); /* make sure initialized */ yuuji@0: return userFlags[i]; yuuji@0: } yuuji@0: yuuji@0: /* Process rc file yuuji@0: * Accepts: file name yuuji@0: * .mminit flag yuuji@0: * Don't use this feature. yuuji@0: */ yuuji@0: yuuji@0: void dorc (char *file,long flag) yuuji@0: { yuuji@0: int i; yuuji@0: char *s,*t,*k,*r,tmp[MAILTMPLEN],tmpx[MAILTMPLEN]; yuuji@0: extern MAILSTREAM CREATEPROTO; yuuji@0: extern MAILSTREAM EMPTYPROTO; yuuji@0: DRIVER *d; yuuji@0: FILE *f; yuuji@0: if ((f = fopen (file ? file : SYSCONFIG,"r")) && yuuji@0: (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))) do { yuuji@0: *t++ = '\0'; /* tie off line, find second space */ yuuji@0: if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) { yuuji@0: *k++ = '\0'; /* tie off two words */ yuuji@0: if (!compare_cstring (s,"set keywords") && !userFlags[0]) { yuuji@0: /* yes, get first keyword */ yuuji@0: k = strtok_r (k,", ",&r); yuuji@0: /* copy keyword list */ yuuji@0: for (i = 0; k && i < NUSERFLAGS; ++i) if (strlen (k) <= MAXUSERFLAG) { yuuji@0: if (userFlags[i]) fs_give ((void **) &userFlags[i]); yuuji@0: userFlags[i] = cpystr (k); yuuji@0: k = strtok_r (NIL,", ",&r); yuuji@0: } yuuji@0: if (flag) break; /* found "set keywords" in .mminit */ yuuji@0: } yuuji@0: yuuji@0: else if (!flag) { /* none of these valid in .mminit */ yuuji@0: if (myUserName) { /* only valid if logged in */ yuuji@0: if (!compare_cstring (s,"set new-mailbox-format") || yuuji@0: !compare_cstring (s,"set new-folder-format")) { yuuji@0: if (!compare_cstring (k,"same-as-inbox")) { yuuji@0: if (d = mail_valid (NIL,"INBOX",NIL)) { yuuji@0: if (!compare_cstring (d->name,"mbox")) yuuji@0: d = (DRIVER *) mail_parameters (NIL,GET_DRIVER, yuuji@0: (void *) "unix"); yuuji@0: else if (!compare_cstring (d->name,"dummy")) d = NIL; yuuji@0: } yuuji@0: createProto = d ? ((*d->open) (NIL)) : &CREATEPROTO; yuuji@0: } yuuji@0: else if (!compare_cstring (k,"system-standard")) yuuji@0: createProto = &CREATEPROTO; yuuji@0: else { /* canonicalize mbox to unix */ yuuji@0: if (!compare_cstring (k,"mbox")) k = "unix"; yuuji@0: /* see if a driver name */ yuuji@0: if (d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,(void *) k)) yuuji@0: createProto = (*d->open) (NIL); yuuji@0: else { /* duh... */ yuuji@0: sprintf (tmpx,"Unknown new mailbox format in %s: %s", yuuji@0: file ? file : SYSCONFIG,k); yuuji@0: MM_LOG (tmpx,WARN); yuuji@0: } yuuji@0: } yuuji@0: } yuuji@0: if (!compare_cstring (s,"set empty-mailbox-format") || yuuji@0: !compare_cstring (s,"set empty-folder-format")) { yuuji@0: if (!compare_cstring (k,"invalid")) appendProto = NIL; yuuji@0: else if (!compare_cstring (k,"same-as-inbox")) yuuji@0: appendProto = ((d = mail_valid (NIL,"INBOX",NIL)) && yuuji@0: compare_cstring (d->name,"dummy")) ? yuuji@0: ((*d->open) (NIL)) : &EMPTYPROTO; yuuji@0: else if (!compare_cstring (k,"system-standard")) yuuji@0: appendProto = &EMPTYPROTO; yuuji@0: else { /* see if a driver name */ yuuji@0: for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL); yuuji@0: d && compare_cstring (d->name,k); d = d->next); yuuji@0: if (d) appendProto = (*d->open) (NIL); yuuji@0: else { /* duh... */ yuuji@0: sprintf (tmpx,"Unknown empty mailbox format in %s: %s", yuuji@0: file ? file : SYSCONFIG,k); yuuji@0: MM_LOG (tmpx,WARN); yuuji@0: } yuuji@0: } yuuji@0: } yuuji@0: } yuuji@0: yuuji@0: if (!compare_cstring (s,"set local-host")) { yuuji@0: fs_give ((void **) &myLocalHost); yuuji@0: myLocalHost = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set news-active-file")) { yuuji@0: fs_give ((void **) &newsActive); yuuji@0: newsActive = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set news-spool-directory")) { yuuji@0: fs_give ((void **) &newsSpool); yuuji@0: newsSpool = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set mh-path")) yuuji@0: mail_parameters (NIL,SET_MHPATH,(void *) k); yuuji@0: else if (!compare_cstring (s,"set mh-allow-inbox")) yuuji@0: mail_parameters (NIL,SET_MHALLOWINBOX,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set news-state-file")) { yuuji@0: fs_give ((void **) &myNewsrc); yuuji@0: myNewsrc = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set ftp-export-directory")) { yuuji@0: fs_give ((void **) &ftpHome); yuuji@0: ftpHome = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set public-home-directory")) { yuuji@0: fs_give ((void **) &publicHome); yuuji@0: publicHome = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set shared-home-directory")) { yuuji@0: fs_give ((void **) &sharedHome); yuuji@0: sharedHome = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set system-inbox")) { yuuji@0: fs_give ((void **) &sysInbox); yuuji@0: sysInbox = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set mail-subdirectory")) { yuuji@0: fs_give ((void **) &mailsubdir); yuuji@0: mailsubdir = cpystr (k); yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set from-widget")) yuuji@0: mail_parameters (NIL,SET_FROMWIDGET, yuuji@0: compare_cstring (k,"header-only") ? yuuji@0: VOIDT : NIL); yuuji@0: yuuji@0: else if (!compare_cstring (s,"set rsh-command")) yuuji@0: mail_parameters (NIL,SET_RSHCOMMAND,(void *) k); yuuji@0: else if (!compare_cstring (s,"set rsh-path")) yuuji@0: mail_parameters (NIL,SET_RSHPATH,(void *) k); yuuji@0: else if (!compare_cstring (s,"set ssh-command")) yuuji@0: mail_parameters (NIL,SET_SSHCOMMAND,(void *) k); yuuji@0: else if (!compare_cstring (s,"set ssh-path")) yuuji@0: mail_parameters (NIL,SET_SSHPATH,(void *) k); yuuji@0: else if (!compare_cstring (s,"set tcp-open-timeout")) yuuji@0: mail_parameters (NIL,SET_OPENTIMEOUT,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set tcp-read-timeout")) yuuji@0: mail_parameters (NIL,SET_READTIMEOUT,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set tcp-write-timeout")) yuuji@0: mail_parameters (NIL,SET_WRITETIMEOUT,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set rsh-timeout")) yuuji@0: mail_parameters (NIL,SET_RSHTIMEOUT,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set ssh-timeout")) yuuji@0: mail_parameters (NIL,SET_SSHTIMEOUT,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set maximum-login-trials")) yuuji@0: mail_parameters (NIL,SET_MAXLOGINTRIALS,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set lookahead")) yuuji@0: mail_parameters (NIL,SET_LOOKAHEAD,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set prefetch")) yuuji@0: mail_parameters (NIL,SET_PREFETCH,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set close-on-error")) yuuji@0: mail_parameters (NIL,SET_CLOSEONERROR,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set imap-port")) yuuji@0: mail_parameters (NIL,SET_IMAPPORT,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set pop3-port")) yuuji@0: mail_parameters (NIL,SET_POP3PORT,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set uid-lookahead")) yuuji@0: mail_parameters (NIL,SET_UIDLOOKAHEAD,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set try-ssl-first")) yuuji@0: mail_parameters (NIL,SET_TRYSSLFIRST,(void *) atol (k)); yuuji@0: yuuji@0: else if (!compare_cstring (s,"set mailbox-protection")) yuuji@0: mbx_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set directory-protection")) yuuji@0: dir_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set lock-protection")) yuuji@0: dotlock_mode = atol (k); yuuji@0: else if (!compare_cstring (s,"set ftp-protection")) yuuji@0: ftp_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set public-protection")) yuuji@0: public_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set shared-protection")) yuuji@0: shared_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set ftp-directory-protection")) yuuji@0: ftp_dir_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set public-directory-protection")) yuuji@0: public_dir_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set shared-directory-protection")) yuuji@0: shared_dir_protection = atol (k); yuuji@0: else if (!compare_cstring (s,"set dot-lock-file-timeout")) yuuji@0: locktimeout = atoi (k); yuuji@0: else if (!compare_cstring (s,"set disable-fcntl-locking")) yuuji@0: fcntlhangbug = atoi (k); yuuji@0: else if (!compare_cstring (s,"set disable-lock-warning")) yuuji@0: disableLockWarning = atoi (k); yuuji@0: else if (!compare_cstring (s,"set disable-unix-UIDs-and-keywords")) yuuji@0: has_no_life = atoi (k); yuuji@0: else if (!compare_cstring (s,"set hide-dot-files")) yuuji@0: hideDotFiles = atoi (k); yuuji@0: else if (!compare_cstring (s,"set list-maximum-level")) yuuji@0: list_max_level = atol (k); yuuji@0: else if (!compare_cstring (s,"set trust-dns")) yuuji@0: mail_parameters (NIL,SET_TRUSTDNS,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set sasl-uses-ptr-name")) yuuji@0: mail_parameters (NIL,SET_SASLUSESPTRNAME,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set network-filesystem-stat-bug")) yuuji@0: netfsstatbug = atoi (k); yuuji@0: else if (!compare_cstring (s,"set nntp-range")) yuuji@0: mail_parameters (NIL,SET_NNTPRANGE,(void *) atol (k)); yuuji@0: yuuji@0: else if (!file) { /* only allowed in system init */ yuuji@0: if (!compare_cstring (s,"set black-box-directory") && yuuji@0: !blackBoxDir) blackBoxDir = cpystr (k); yuuji@0: else if (!compare_cstring(s,"set black-box-default-home-directory")&& yuuji@0: blackBoxDir && !blackBoxDefaultHome) yuuji@0: blackBoxDefaultHome = cpystr (k); yuuji@0: else if (!compare_cstring (s,"set anonymous-home-directory") && yuuji@0: !anonymousHome) anonymousHome = cpystr (k); yuuji@0: /* It's tempting to allow setting the CA path yuuji@0: * in a user init. However, that opens up a yuuji@0: * vector of attack big enough to drive a yuuji@0: * truck through... Resist the temptation. yuuji@0: */ yuuji@0: else if (!compare_cstring (s,"set CA-certificate-path")) yuuji@0: sslCApath = cpystr (k); yuuji@0: else if (!compare_cstring (s,"set disable-plaintext")) yuuji@0: disablePlaintext = atoi (k); yuuji@0: else if (!compare_cstring (s,"set allowed-login-attempts")) yuuji@0: logtry = atoi (k); yuuji@0: else if (!compare_cstring (s,"set chroot-server")) yuuji@0: closedBox = atoi (k); yuuji@0: else if (!compare_cstring (s,"set restrict-mailbox-access")) yuuji@0: for (k = strtok_r (k,", ",&r); k; k = strtok_r (NIL,", ",&r)) { yuuji@0: if (!compare_cstring (k,"root")) restrictBox |= RESTRICTROOT; yuuji@0: else if (!compare_cstring (k,"otherusers")) yuuji@0: restrictBox |= RESTRICTOTHERUSER; yuuji@0: else if (!compare_cstring (k,"all")) restrictBox = -1; yuuji@0: } yuuji@0: else if (!compare_cstring (s,"set advertise-the-world")) yuuji@0: advertisetheworld = atoi (k); yuuji@0: else if (!compare_cstring (s,"set limited-advertise")) yuuji@0: limitedadvertise = atoi (k); yuuji@0: else if (!compare_cstring yuuji@0: (s,"set disable-automatic-shared-namespaces")) yuuji@0: noautomaticsharedns = atoi (k); yuuji@0: else if (!compare_cstring (s,"set allow-user-config")) yuuji@0: allowuserconfig = atoi (k); yuuji@0: else if (!compare_cstring (s,"set allow-reverse-dns")) yuuji@0: mail_parameters (NIL,SET_ALLOWREVERSEDNS,(void *) atol (k)); yuuji@0: else if (!compare_cstring (s,"set k5-cp-uses-service-name")) yuuji@0: kerb_cp_svr_name = atoi (k); yuuji@0: /* must appear in file after any yuuji@0: * "set disable-plaintext" command! */ yuuji@0: else if (!compare_cstring (s,"set plaintext-allowed-clients")) { yuuji@0: for (k = strtok_r (k,", ",&r); k && !tcp_isclienthost (k); yuuji@0: k = strtok_r (NIL,", ",&r)); yuuji@0: if (k) disablePlaintext = 0; yuuji@0: } yuuji@0: } yuuji@0: } yuuji@0: } yuuji@0: } while ((s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))); yuuji@0: if (f) fclose (f); /* flush the file */ yuuji@0: } yuuji@0: yuuji@0: /* INBOX create function for tmail/dmail use only yuuji@0: * Accepts: mail stream yuuji@0: * path name buffer, preloaded with driver-dependent path yuuji@0: * Returns: T on success, NIL on failure yuuji@0: * yuuji@0: * This routine is evil and a truly incredible kludge. It is private for yuuji@0: * tmail/dmail and is not supported for any other application. yuuji@0: */ yuuji@0: yuuji@0: long path_create (MAILSTREAM *stream,char *path) yuuji@0: { yuuji@0: long ret; yuuji@0: short rsave = restrictBox; yuuji@0: restrictBox = NIL; /* can't restrict */ yuuji@0: if (blackBox) { /* if black box */ yuuji@0: /* toss out driver dependent names */ yuuji@3: sprintf (path,"%s/INBOX",mymailboxdir ()); yuuji@0: blackBox = NIL; /* well that's evil - evil is going on */ yuuji@0: ret = mail_create (stream,path); yuuji@0: blackBox = T; /* restore the box */ yuuji@0: } yuuji@0: /* easy thing otherwise */ yuuji@0: else ret = mail_create (stream,path); yuuji@0: restrictBox = rsave; /* restore restrictions */ yuuji@0: return ret; yuuji@0: } yuuji@0: yuuji@0: /* Default block notify routine yuuji@0: * Accepts: reason for calling yuuji@0: * data yuuji@0: * Returns: data yuuji@0: */ yuuji@0: yuuji@0: void *mm_blocknotify (int reason,void *data) yuuji@0: { yuuji@0: void *ret = data; yuuji@0: switch (reason) { yuuji@0: case BLOCK_SENSITIVE: /* entering sensitive code */ yuuji@0: ret = (void *) (unsigned long) alarm (0); yuuji@0: break; yuuji@0: case BLOCK_NONSENSITIVE: /* exiting sensitive code */ yuuji@0: if ((unsigned long) data) alarm ((unsigned long) data); yuuji@0: break; yuuji@0: default: /* ignore all other reasons */ yuuji@0: break; yuuji@0: } yuuji@0: return ret; yuuji@0: }