0
|
1 /* ========================================================================
|
|
2 * Copyright 1988-2008 University of Washington
|
|
3 *
|
|
4 * Licensed under the Apache License, Version 2.0 (the "License");
|
|
5 * you may not use this file except in compliance with the License.
|
|
6 * You may obtain a copy of the License at
|
|
7 *
|
|
8 * http://www.apache.org/licenses/LICENSE-2.0
|
|
9 *
|
|
10 *
|
|
11 * ========================================================================
|
|
12 */
|
|
13
|
|
14 /*
|
|
15 * Program: UNIX environment routines
|
|
16 *
|
|
17 * Author: Mark Crispin
|
|
18 * UW Technology
|
|
19 * University of Washington
|
|
20 * Seattle, WA 98195
|
|
21 * Internet: MRC@Washington.EDU
|
|
22 *
|
|
23 * Date: 1 August 1988
|
3
|
24 * Last Edited: 23 February 2009
|
0
|
25 */
|
|
26
|
|
27 #include <grp.h>
|
|
28 #include <signal.h>
|
|
29 #include <sys/wait.h>
|
|
30
|
|
31
|
|
32 /* in case stat.h is ancient */
|
|
33
|
|
34 #ifndef S_IRUSR
|
|
35 #define S_IRUSR S_IREAD
|
|
36 #endif
|
|
37 #ifndef S_IWUSR
|
|
38 #define S_IWUSR S_IWRITE
|
|
39 #endif
|
|
40 #ifndef S_IXUSR
|
|
41 #define S_IXUSR S_IEXEC
|
|
42 #endif
|
|
43 #ifndef S_IRGRP
|
|
44 #define S_IRGRP (S_IREAD >> 3)
|
|
45 #endif
|
|
46 #ifndef S_IWGRP
|
|
47 #define S_IWGRP (S_IWRITE >> 3)
|
|
48 #endif
|
|
49 #ifndef S_IXGRP
|
|
50 #define S_IXGRP (S_IEXEC >> 3)
|
|
51 #endif
|
|
52 #ifndef S_IROTH
|
|
53 #define S_IROTH (S_IREAD >> 6)
|
|
54 #endif
|
|
55 #ifndef S_IWOTH
|
|
56 #define S_IWOTH (S_IWRITE >> 6)
|
|
57 #endif
|
|
58 #ifndef S_IXOTH
|
|
59 #define S_IXOTH (S_IEXEC >> 6)
|
|
60 #endif
|
|
61
|
|
62 /* c-client environment parameters */
|
|
63
|
|
64 static char *myUserName = NIL; /* user name */
|
|
65 static char *myHomeDir = NIL; /* home directory name */
|
|
66 static char *myServerName = NIL;/* server name */
|
|
67 static char *myLocalHost = NIL; /* local host name */
|
|
68 static char *myNewsrc = NIL; /* newsrc file name */
|
|
69 static char *mailsubdir = NIL; /* mailbox subdirectory name */
|
|
70 static char *sysInbox = NIL; /* system inbox name */
|
|
71 static char *newsActive = NIL; /* news active file */
|
|
72 static char *newsSpool = NIL; /* news spool */
|
|
73 static char *blackBoxDir = NIL; /* black box directory name */
|
|
74 /* black box default home directory */
|
|
75 static char *blackBoxDefaultHome = NIL;
|
|
76 static char *sslCApath = NIL; /* non-standard CA path */
|
|
77 static short anonymous = NIL; /* is anonymous */
|
|
78 static short blackBox = NIL; /* is a black box */
|
|
79 static short closedBox = NIL; /* is a closed box (uses chroot() jail) */
|
|
80 static short restrictBox = NIL; /* is a restricted box */
|
|
81 static short has_no_life = NIL; /* is a cretin with no life */
|
|
82 /* block environment init */
|
|
83 static short block_env_init = NIL;
|
|
84 static short hideDotFiles = NIL;/* hide files whose names start with . */
|
|
85 /* advertise filesystem root */
|
|
86 static short advertisetheworld = NIL;
|
|
87 /* only advertise own mailboxes and #shared */
|
|
88 static short limitedadvertise = NIL;
|
|
89 /* disable automatic shared namespaces */
|
|
90 static short noautomaticsharedns = NIL;
|
|
91 static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
|
|
92 /* client principals include service name */
|
|
93 static short kerb_cp_svr_name = NIL;
|
|
94 static long locktimeout = 5; /* default lock timeout in minutes */
|
|
95 /* default prototypes */
|
|
96 static MAILSTREAM *createProto = NIL;
|
|
97 static MAILSTREAM *appendProto = NIL;
|
|
98 /* default user flags */
|
|
99 static char *userFlags[NUSERFLAGS] = {NIL};
|
|
100 static NAMESPACE *nslist[3]; /* namespace list */
|
|
101 static int logtry = 3; /* number of server login tries */
|
|
102 /* block notification */
|
|
103 static blocknotify_t mailblocknotify = mm_blocknotify;
|
|
104 /* logout function */
|
|
105 static logouthook_t maillogouthook = NIL;
|
|
106 /* logout data */
|
|
107 static void *maillogoutdata = NIL;
|
|
108 /* allow user config files */
|
|
109 static short allowuserconfig = NIL;
|
|
110 /* 1 = disable plaintext, 2 = if not SSL */
|
|
111 static long disablePlaintext = NIL;
|
|
112 static long list_max_level = 20;/* maximum level of list recursion */
|
|
113 /* facility for syslog */
|
|
114 static int syslog_facility = LOG_MAIL;
|
|
115
|
|
116 /* Path of the privileged system lock program (mlock). Normally set by
|
|
117 * logic test.
|
|
118 */
|
|
119
|
|
120 static char *lockpgm = LOCKPGM;
|
|
121
|
|
122 /* Directory used for shared locks. MUST be the same for all users of the
|
|
123 * system, and MUST be protected 1777. /var/tmp may be preferable on some
|
|
124 * systems.
|
|
125 */
|
|
126
|
|
127 static const char *tmpdir = "/tmp";
|
|
128
|
|
129 /* Do not change shlock_mode. Doing so can cause mailbox corruption and
|
|
130 * denial of service. It also defeats the entire purpose of the shared
|
|
131 * lock mechanism. The right way to avoid shared locks is to set up a
|
|
132 * closed box (see the closedBox setting).
|
|
133 */
|
|
134
|
|
135 /* shared lock mode */
|
|
136 static const int shlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
|
|
137
|
|
138
|
|
139 /* It is STRONGLY recommended that you do not change dotlock_mode. Doing so
|
|
140 * can cause denial of service with old dot-lock files left lying around.
|
|
141 * However, since dot-locks are only used with traditional UNIX and MMDF
|
|
142 * formats which are not normally shared, it is much less harmful to tamper
|
|
143 * with this than with shlock_mode.
|
|
144 */
|
|
145
|
|
146 /* dot-lock mode */
|
|
147 static long dotlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
|
|
148
|
|
149 /* File/directory access and protection policies */
|
|
150
|
|
151 /* Unlike shlock_mode, the ????_protection modes are intended to be fully
|
|
152 * customizable according to site policy. The values here are recommended
|
|
153 * settings, based upon the documented purposes of the namespaces.
|
|
154 */
|
|
155
|
|
156 /* user space - only owner can read/write */
|
|
157 static char *myMailboxDir = NIL;/* user space directory name */
|
|
158 /* default file protection */
|
|
159 static long mbx_protection = S_IRUSR|S_IWUSR;
|
|
160 /* default directory protection */
|
|
161 static long dir_protection = S_IRUSR|S_IWUSR|S_IXUSR;
|
|
162
|
|
163 /* user space for user "anonymous" */
|
|
164 /* anonymous home directory */
|
|
165 static char *anonymousHome = NIL;
|
|
166
|
|
167 /* #ftp - everybody can read, only owner can write */
|
|
168 static char *ftpHome = NIL; /* ftp export home directory */
|
|
169 /* default ftp file protection */
|
|
170 static long ftp_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
|
|
171 static long ftp_dir_protection =/* default ftp directory protection */
|
|
172 S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
|
|
173
|
|
174 /* #public - everybody can read/write */
|
|
175 static char *publicHome = NIL; /* public home directory */
|
|
176 static long public_protection = /* default public file protection */
|
|
177 S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
|
|
178 /* default public directory protection */
|
|
179 static long public_dir_protection =
|
|
180 S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH;
|
|
181
|
|
182 /* #shared/ - owner and group members can read/write */
|
|
183 static char *sharedHome = NIL; /* shared home directory */
|
|
184 /* default shared file protection */
|
|
185 static long shared_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
|
|
186 /* default shared directory protection */
|
|
187 static long shared_dir_protection =
|
|
188 S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP;
|
|
189
|
|
190 /* OS bug workarounds - should be avoided at all cost */
|
|
191
|
|
192
|
|
193 /* Don't set fcntlhangbug unless you really have to, since it risks mailbox
|
|
194 * corruption. The flocksim.c mechanism is designed to detect NFS access
|
|
195 * and no-op in that cases only, so this flag should be unnecessary.
|
|
196 */
|
|
197
|
|
198 static short fcntlhangbug = NIL;/* flock() emulator using fcntl() is a no-op */
|
|
199
|
|
200
|
|
201 /* Don't set netfsstatbug unless you really have to, since it dramatically
|
|
202 * slows down traditional UNIX and MMDF mailbox performance.
|
|
203 */
|
|
204
|
|
205 static short netfsstatbug = NIL;/* compensate for broken stat() on network
|
|
206 * filesystems (AFS and old NFS)
|
|
207 */
|
|
208
|
|
209
|
|
210 /* Note: setting disableLockWarning means that you assert that the
|
|
211 * so-modified copy of this software will NEVER be used:
|
|
212 * 1) in conjunction with any software which expects .lock files
|
|
213 * 2) to access NFS-mounted files and directories
|
|
214 *
|
|
215 * Unless both of these conditions apply, then do not set this flag.
|
|
216 * Instead, read the FAQ (item 7.10) and either use 1777 protection
|
|
217 * on the mail spool, or install mlock.
|
|
218 *
|
|
219 * In addition, by setting this flag you also agree that you are fully
|
|
220 * legally and morally responsible when (not if) mail files are damaged
|
|
221 * as the result of your choice.
|
|
222 *
|
|
223 * The mlock tool exists for a reason. Use it.
|
|
224 */
|
|
225 /* disable warning if can't make .lock file */
|
|
226 static short disableLockWarning = NIL;
|
|
227
|
|
228 /* UNIX Namespaces */
|
|
229
|
|
230 /* personal mh namespace */
|
|
231 static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL};
|
|
232 static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf};
|
|
233 /* home namespace */
|
|
234 static NAMESPACE nshome = {"",'/',NIL,&nsmh};
|
|
235 /* UNIX other user namespace */
|
|
236 static NAMESPACE nsunixother = {"~",'/',NIL,NIL};
|
|
237 /* black box other user namespace */
|
|
238 static NAMESPACE nsblackother = {"/",'/',NIL,NIL};
|
|
239 /* public (anonymous OK) namespace */
|
|
240 static NAMESPACE nspublic = {"#public/",'/',NIL,NIL};
|
|
241 /* netnews namespace */
|
|
242 static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic};
|
|
243 /* FTP export namespace */
|
|
244 static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews};
|
|
245 /* shared (no anonymous) namespace */
|
|
246 static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp};
|
|
247 /* world namespace */
|
|
248 static NAMESPACE nsworld = {"/",'/',NIL,&nsshared};
|
|
249 /* only shared and public namespaces */
|
|
250 static NAMESPACE nslimited = {"#shared/",'/',NIL,&nspublic};
|
|
251
|
|
252
|
|
253
|
|
254 #include "write.c" /* include safe writing routines */
|
|
255 #include "crexcl.c" /* include exclusive create */
|
|
256 #include "pmatch.c" /* include wildcard pattern matcher */
|
|
257
|
|
258 /* Get all authenticators */
|
|
259
|
|
260 #include "auths.c"
|
|
261
|
|
262 /* Environment manipulate parameters
|
|
263 * Accepts: function code
|
|
264 * function-dependent value
|
|
265 * Returns: function-dependent return value
|
|
266 */
|
|
267
|
|
268 void *env_parameters (long function,void *value)
|
|
269 {
|
|
270 void *ret = NIL;
|
|
271 switch ((int) function) {
|
|
272 case GET_NAMESPACE:
|
|
273 ret = (void *) nslist;
|
|
274 break;
|
|
275 case SET_USERNAME:
|
|
276 if (myUserName) fs_give ((void **) &myUserName);
|
|
277 myUserName = cpystr ((char *) value);
|
|
278 case GET_USERNAME:
|
|
279 ret = (void *) myUserName;
|
|
280 break;
|
|
281 case SET_HOMEDIR:
|
|
282 if (myHomeDir) fs_give ((void **) &myHomeDir);
|
|
283 myHomeDir = cpystr ((char *) value);
|
|
284 case GET_HOMEDIR:
|
|
285 ret = (void *) myHomeDir;
|
|
286 break;
|
|
287 case SET_LOCALHOST:
|
|
288 if (myLocalHost) fs_give ((void **) &myLocalHost);
|
|
289 myLocalHost = cpystr ((char *) value);
|
|
290 case GET_LOCALHOST:
|
|
291 ret = (void *) myLocalHost;
|
|
292 break;
|
|
293 case SET_NEWSRC:
|
|
294 if (myNewsrc) fs_give ((void **) &myNewsrc);
|
|
295 myNewsrc = cpystr ((char *) value);
|
|
296 case GET_NEWSRC:
|
|
297 ret = (void *) myNewsrc;
|
|
298 break;
|
|
299 case SET_NEWSACTIVE:
|
|
300 if (newsActive) fs_give ((void **) &newsActive);
|
|
301 newsActive = cpystr ((char *) value);
|
|
302 case GET_NEWSACTIVE:
|
|
303 ret = (void *) newsActive;
|
|
304 break;
|
|
305 case SET_NEWSSPOOL:
|
|
306 if (newsSpool) fs_give ((void **) &newsSpool);
|
|
307 newsSpool = cpystr ((char *) value);
|
|
308 case GET_NEWSSPOOL:
|
|
309 ret = (void *) newsSpool;
|
|
310 break;
|
|
311
|
|
312 case SET_ANONYMOUSHOME:
|
|
313 if (anonymousHome) fs_give ((void **) &anonymousHome);
|
|
314 anonymousHome = cpystr ((char *) value);
|
|
315 case GET_ANONYMOUSHOME:
|
|
316 if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME);
|
|
317 ret = (void *) anonymousHome;
|
|
318 break;
|
|
319 case SET_FTPHOME:
|
|
320 if (ftpHome) fs_give ((void **) &ftpHome);
|
|
321 ftpHome = cpystr ((char *) value);
|
|
322 case GET_FTPHOME:
|
|
323 ret = (void *) ftpHome;
|
|
324 break;
|
|
325 case SET_PUBLICHOME:
|
|
326 if (publicHome) fs_give ((void **) &publicHome);
|
|
327 publicHome = cpystr ((char *) value);
|
|
328 case GET_PUBLICHOME:
|
|
329 ret = (void *) publicHome;
|
|
330 break;
|
|
331 case SET_SHAREDHOME:
|
|
332 if (sharedHome) fs_give ((void **) &sharedHome);
|
|
333 sharedHome = cpystr ((char *) value);
|
|
334 case GET_SHAREDHOME:
|
|
335 ret = (void *) sharedHome;
|
|
336 break;
|
|
337 case SET_SYSINBOX:
|
|
338 if (sysInbox) fs_give ((void **) &sysInbox);
|
|
339 sysInbox = cpystr ((char *) value);
|
|
340 case GET_SYSINBOX:
|
|
341 ret = (void *) sysInbox;
|
|
342 break;
|
|
343 case SET_SSLCAPATH: /* this can be set null */
|
|
344 if (sslCApath) fs_give ((void **) &sslCApath);
|
|
345 sslCApath = value ? cpystr ((char *) value) : value;
|
|
346 break;
|
|
347 case GET_SSLCAPATH:
|
|
348 ret = (void *) sslCApath;
|
|
349 break;
|
|
350 case SET_LISTMAXLEVEL:
|
|
351 list_max_level = (long) value;
|
|
352 case GET_LISTMAXLEVEL:
|
|
353 ret = (void *) list_max_level;
|
|
354 break;
|
|
355
|
|
356 case SET_MBXPROTECTION:
|
|
357 mbx_protection = (long) value;
|
|
358 case GET_MBXPROTECTION:
|
|
359 ret = (void *) mbx_protection;
|
|
360 break;
|
|
361 case SET_DIRPROTECTION:
|
|
362 dir_protection = (long) value;
|
|
363 case GET_DIRPROTECTION:
|
|
364 ret = (void *) dir_protection;
|
|
365 break;
|
|
366 case SET_LOCKPROTECTION:
|
|
367 dotlock_mode = (long) value;
|
|
368 case GET_LOCKPROTECTION:
|
|
369 ret = (void *) dotlock_mode;
|
|
370 break;
|
|
371 case SET_FTPPROTECTION:
|
|
372 ftp_protection = (long) value;
|
|
373 case GET_FTPPROTECTION:
|
|
374 ret = (void *) ftp_protection;
|
|
375 break;
|
|
376 case SET_PUBLICPROTECTION:
|
|
377 public_protection = (long) value;
|
|
378 case GET_PUBLICPROTECTION:
|
|
379 ret = (void *) public_protection;
|
|
380 break;
|
|
381 case SET_SHAREDPROTECTION:
|
|
382 shared_protection = (long) value;
|
|
383 case GET_SHAREDPROTECTION:
|
|
384 ret = (void *) shared_protection;
|
|
385 break;
|
|
386 case SET_FTPDIRPROTECTION:
|
|
387 ftp_dir_protection = (long) value;
|
|
388 case GET_FTPDIRPROTECTION:
|
|
389 ret = (void *) ftp_dir_protection;
|
|
390 break;
|
|
391 case SET_PUBLICDIRPROTECTION:
|
|
392 public_dir_protection = (long) value;
|
|
393 case GET_PUBLICDIRPROTECTION:
|
|
394 ret = (void *) public_dir_protection;
|
|
395 break;
|
|
396 case SET_SHAREDDIRPROTECTION:
|
|
397 shared_dir_protection = (long) value;
|
|
398 case GET_SHAREDDIRPROTECTION:
|
|
399 ret = (void *) shared_dir_protection;
|
|
400 break;
|
|
401
|
|
402 case SET_LOCKTIMEOUT:
|
|
403 locktimeout = (long) value;
|
|
404 case GET_LOCKTIMEOUT:
|
|
405 ret = (void *) locktimeout;
|
|
406 break;
|
|
407 case SET_DISABLEFCNTLLOCK:
|
|
408 fcntlhangbug = value ? T : NIL;
|
|
409 case GET_DISABLEFCNTLLOCK:
|
|
410 ret = (void *) (fcntlhangbug ? VOIDT : NIL);
|
|
411 break;
|
|
412 case SET_LOCKEACCESERROR:
|
|
413 disableLockWarning = value ? NIL : T;
|
|
414 case GET_LOCKEACCESERROR:
|
|
415 ret = (void *) (disableLockWarning ? NIL : VOIDT);
|
|
416 break;
|
|
417 case SET_HIDEDOTFILES:
|
|
418 hideDotFiles = value ? T : NIL;
|
|
419 case GET_HIDEDOTFILES:
|
|
420 ret = (void *) (hideDotFiles ? VOIDT : NIL);
|
|
421 break;
|
|
422 case SET_DISABLEPLAINTEXT:
|
|
423 disablePlaintext = (long) value;
|
|
424 case GET_DISABLEPLAINTEXT:
|
|
425 ret = (void *) disablePlaintext;
|
|
426 break;
|
|
427 case SET_CHROOTSERVER:
|
|
428 closedBox = value ? T : NIL;
|
|
429 case GET_CHROOTSERVER:
|
|
430 ret = (void *) (closedBox ? VOIDT : NIL);
|
|
431 break;
|
|
432 case SET_ADVERTISETHEWORLD:
|
|
433 advertisetheworld = value ? T : NIL;
|
|
434 case GET_ADVERTISETHEWORLD:
|
|
435 ret = (void *) (advertisetheworld ? VOIDT : NIL);
|
|
436 break;
|
|
437 case SET_LIMITEDADVERTISE:
|
|
438 limitedadvertise = value ? T : NIL;
|
|
439 case GET_LIMITEDADVERTISE:
|
|
440 ret = (void *) (limitedadvertise ? VOIDT : NIL);
|
|
441 break;
|
|
442 case SET_DISABLEAUTOSHAREDNS:
|
|
443 noautomaticsharedns = value ? T : NIL;
|
|
444 case GET_DISABLEAUTOSHAREDNS:
|
|
445 ret = (void *) (noautomaticsharedns ? VOIDT : NIL);
|
|
446 break;
|
|
447 case SET_DISABLE822TZTEXT:
|
|
448 no822tztext = value ? T : NIL;
|
|
449 case GET_DISABLE822TZTEXT:
|
|
450 ret = (void *) (no822tztext ? VOIDT : NIL);
|
|
451 break;
|
|
452
|
|
453 case SET_USERHASNOLIFE:
|
|
454 has_no_life = value ? T : NIL;
|
|
455 case GET_USERHASNOLIFE:
|
|
456 ret = (void *) (has_no_life ? VOIDT : NIL);
|
|
457 break;
|
|
458 case SET_KERBEROS_CP_SVR_NAME:
|
|
459 kerb_cp_svr_name = value ? T : NIL;
|
|
460 case GET_KERBEROS_CP_SVR_NAME:
|
|
461 ret = (void *) (kerb_cp_svr_name ? VOIDT : NIL);
|
|
462 break;
|
|
463 case SET_NETFSSTATBUG:
|
|
464 netfsstatbug = value ? T : NIL;
|
|
465 case GET_NETFSSTATBUG:
|
|
466 ret = (void *) (netfsstatbug ? VOIDT : NIL);
|
|
467 break;
|
|
468 case SET_BLOCKENVINIT:
|
|
469 block_env_init = value ? T : NIL;
|
|
470 case GET_BLOCKENVINIT:
|
|
471 ret = (void *) (block_env_init ? VOIDT : NIL);
|
|
472 break;
|
|
473 case SET_BLOCKNOTIFY:
|
|
474 mailblocknotify = (blocknotify_t) value;
|
|
475 case GET_BLOCKNOTIFY:
|
|
476 ret = (void *) mailblocknotify;
|
|
477 break;
|
|
478 case SET_LOGOUTHOOK:
|
|
479 maillogouthook = (logouthook_t) value;
|
|
480 case GET_LOGOUTHOOK:
|
|
481 ret = maillogouthook;
|
|
482 break;
|
|
483 case SET_LOGOUTDATA:
|
|
484 maillogoutdata = (void *) value;
|
|
485 case GET_LOGOUTDATA:
|
|
486 ret = maillogoutdata;
|
|
487 }
|
|
488 return ret;
|
|
489 }
|
|
490
|
|
491 /* Write current time
|
|
492 * Accepts: destination string
|
|
493 * optional format of day-of-week prefix
|
|
494 * format of date and time
|
|
495 * flag whether to append symbolic timezone
|
|
496 */
|
|
497
|
|
498 static void do_date (char *date,char *prefix,char *fmt,int suffix)
|
|
499 {
|
|
500 time_t tn = time (0);
|
|
501 struct tm *t = gmtime (&tn);
|
|
502 int zone = t->tm_hour * 60 + t->tm_min;
|
|
503 int julian = t->tm_yday;
|
|
504 t = localtime (&tn); /* get local time now */
|
|
505 /* minus UTC minutes since midnight */
|
|
506 zone = t->tm_hour * 60 + t->tm_min - zone;
|
|
507 /* julian can be one of:
|
|
508 * 36x local time is December 31, UTC is January 1, offset -24 hours
|
|
509 * 1 local time is 1 day ahead of UTC, offset +24 hours
|
|
510 * 0 local time is same day as UTC, no offset
|
|
511 * -1 local time is 1 day behind UTC, offset -24 hours
|
|
512 * -36x local time is January 1, UTC is December 31, offset +24 hours
|
|
513 */
|
|
514 if (julian = t->tm_yday -julian)
|
|
515 zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
|
|
516 if (prefix) { /* want day of week? */
|
|
517 sprintf (date,prefix,days[t->tm_wday]);
|
|
518 date += strlen (date); /* make next sprintf append */
|
|
519 }
|
|
520 /* output the date */
|
|
521 sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
|
|
522 t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
|
|
523 /* append timezone suffix if desired */
|
|
524 if (suffix) rfc822_timezone (date,(void *) t);
|
|
525 }
|
|
526
|
|
527 /* Write current time in RFC 822 format
|
|
528 * Accepts: destination string
|
|
529 */
|
|
530
|
|
531 void rfc822_date (char *date)
|
|
532 {
|
|
533 do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
|
|
534 no822tztext ? NIL : T);
|
|
535 }
|
|
536
|
|
537
|
|
538 /* Write current time in fixed-width RFC 822 format
|
|
539 * Accepts: destination string
|
|
540 */
|
|
541
|
|
542 void rfc822_fixed_date (char *date)
|
|
543 {
|
|
544 do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
|
|
545 }
|
|
546
|
|
547
|
|
548 /* Write current time in internal format
|
|
549 * Accepts: destination string
|
|
550 */
|
|
551
|
|
552 void internal_date (char *date)
|
|
553 {
|
|
554 do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
|
|
555 }
|
|
556
|
|
557 /* Initialize server
|
|
558 * Accepts: server name for syslog or NIL
|
|
559 * /etc/services service name or NIL
|
|
560 * alternate /etc/services service name or NIL
|
|
561 * clock interrupt handler
|
|
562 * kiss-of-death interrupt handler
|
|
563 * hangup interrupt handler
|
|
564 * termination interrupt handler
|
|
565 */
|
|
566
|
|
567 void server_init (char *server,char *service,char *sslservice,
|
|
568 void *clkint,void *kodint,void *hupint,void *trmint,
|
|
569 void *staint)
|
|
570 {
|
|
571 int onceonly = server && service && sslservice;
|
|
572 if (onceonly) { /* set server name in syslog */
|
|
573 int mask;
|
|
574 openlog (myServerName = cpystr (server),LOG_PID,syslog_facility);
|
|
575 fclose (stderr); /* possibly save a process ID */
|
|
576 dorc (NIL,NIL); /* do systemwide configuration */
|
|
577 switch (mask = umask (022)){/* check old umask */
|
|
578 case 0: /* definitely unreasonable */
|
|
579 case 022: /* don't need to change it */
|
|
580 break;
|
|
581 default: /* already was a reasonable value */
|
|
582 umask (mask); /* so change it back */
|
|
583 }
|
|
584 }
|
|
585 arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */
|
|
586 arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */
|
|
587 arm_signal (SIGHUP,hupint); /* prepare for hangup */
|
|
588 arm_signal (SIGPIPE,hupint); /* alternative hangup */
|
|
589 arm_signal (SIGTERM,trmint); /* prepare for termination */
|
|
590 /* status dump */
|
|
591 if (staint) arm_signal (SIGUSR1,staint);
|
|
592 if (onceonly) { /* set up network and maybe SSL */
|
|
593 long port;
|
|
594 struct servent *sv;
|
|
595 /* Use SSL if SSL service, or if server starts with "s" and not service */
|
|
596 if (((port = tcp_serverport ()) >= 0)) {
|
|
597 if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
|
|
598 syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
|
|
599 else if ((sv = getservbyname (sslservice,"tcp")) &&
|
|
600 (port == ntohs (sv->s_port))) {
|
|
601 syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
|
|
602 tcp_clientaddr ());
|
|
603 ssl_server_init (server);
|
|
604 }
|
|
605 else { /* not service or SSL service port */
|
|
606 syslog (LOG_DEBUG,"port %ld service init from %s",port,
|
|
607 tcp_clientaddr ());
|
|
608 if (*server == 's') ssl_server_init (server);
|
|
609 }
|
|
610 }
|
|
611 }
|
|
612 }
|
|
613
|
|
614 /* Wait for stdin input
|
|
615 * Accepts: timeout in seconds
|
|
616 * Returns: T if have input on stdin, else NIL
|
|
617 */
|
|
618
|
|
619 long server_input_wait (long seconds)
|
|
620 {
|
|
621 fd_set rfd,efd;
|
|
622 struct timeval tmo;
|
|
623 FD_ZERO (&rfd);
|
|
624 FD_ZERO (&efd);
|
|
625 FD_SET (0,&rfd);
|
|
626 FD_SET (0,&efd);
|
|
627 tmo.tv_sec = seconds; tmo.tv_usec = 0;
|
|
628 return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
|
|
629 }
|
|
630
|
|
631 /* Return UNIX password entry for user name
|
|
632 * Accepts: user name string
|
|
633 * Returns: password entry
|
|
634 *
|
|
635 * Tries all-lowercase form of user name if given user name fails
|
|
636 */
|
|
637
|
|
638 static struct passwd *pwuser (unsigned char *user)
|
|
639 {
|
|
640 unsigned char *s;
|
|
641 struct passwd *pw = getpwnam (user);
|
|
642 if (!pw) { /* failed, see if any uppercase characters */
|
|
643 for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
|
|
644 if (*s) { /* yes, try all lowercase form */
|
|
645 pw = getpwnam (s = lcase (cpystr (user)));
|
|
646 fs_give ((void **) &s);
|
|
647 }
|
|
648 }
|
|
649 return pw;
|
|
650 }
|
|
651
|
|
652
|
|
653 /* Validate password for user name
|
|
654 * Accepts: user name string
|
|
655 * password string
|
|
656 * argument count
|
|
657 * argument vector
|
|
658 * Returns: password entry if validated
|
|
659 *
|
|
660 * Tries password+1 if password fails and starts with space
|
|
661 */
|
|
662
|
|
663 static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[])
|
|
664 {
|
|
665 char *s;
|
|
666 struct passwd *pw;
|
|
667 struct passwd *ret = NIL;
|
|
668 if (auth_md5.server) { /* using CRAM-MD5 authentication? */
|
|
669 if (s = auth_md5_pwd (user)) {
|
|
670 if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1)))
|
|
671 ret = pwuser (user); /* validated, get passwd entry for user */
|
|
672 memset (s,0,strlen (s)); /* erase sensitive information */
|
|
673 fs_give ((void **) &s);
|
|
674 }
|
|
675 }
|
3
|
676 else if (pw = pwuser (user)) {/* can get user? */
|
0
|
677 s = cpystr (pw->pw_name); /* copy returned name in case we need it */
|
|
678 if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) &&
|
|
679 (*pwd == ' ') && pwd[1] && (ret = pwuser (s)))
|
|
680 ret = checkpw (pw,pwd+1,argc,argv);
|
|
681 fs_give ((void **) &s); /* don't need copy of name any more */
|
|
682 }
|
|
683 return ret;
|
|
684 }
|
|
685
|
|
686 /* Server log in
|
|
687 * Accepts: user name string
|
|
688 * password string
|
|
689 * authenticating user name string
|
|
690 * argument count
|
|
691 * argument vector
|
|
692 * Returns: T if password validated, NIL otherwise
|
|
693 */
|
|
694
|
|
695 long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[])
|
|
696 {
|
|
697 struct passwd *pw = NIL;
|
|
698 int level = LOG_NOTICE;
|
|
699 char *err = "failed";
|
|
700 /* cretins still haven't given up */
|
|
701 if ((strlen (user) >= NETMAXUSER) ||
|
|
702 (authuser && (strlen (authuser) >= NETMAXUSER))) {
|
|
703 level = LOG_ALERT; /* escalate this alert */
|
|
704 err = "SYSTEM BREAK-IN ATTEMPT";
|
|
705 logtry = 0; /* render this session useless */
|
|
706 }
|
|
707 else if (logtry-- <= 0) err = "excessive login failures";
|
|
708 else if (disablePlaintext) err = "disabled";
|
|
709 else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv);
|
|
710 else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user);
|
|
711 if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T;
|
|
712 syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err,
|
|
713 user,(authuser && *authuser) ? authuser : user,tcp_clienthost ());
|
|
714 sleep (3); /* slow down possible cracker */
|
|
715 return NIL;
|
|
716 }
|
|
717
|
|
718 /* Authenticated server log in
|
|
719 * Accepts: user name string
|
|
720 * authenticating user name string
|
|
721 * argument count
|
|
722 * argument vector
|
|
723 * Returns: T if password validated, NIL otherwise
|
|
724 */
|
|
725
|
|
726 long authserver_login (char *user,char *authuser,int argc,char *argv[])
|
|
727 {
|
|
728 return pw_login (pwuser (user),authuser,user,NIL,argc,argv);
|
|
729 }
|
|
730
|
|
731
|
|
732 /* Log in as anonymous daemon
|
|
733 * Accepts: argument count
|
|
734 * argument vector
|
|
735 * Returns: T if successful, NIL if error
|
|
736 */
|
|
737
|
|
738 long anonymous_login (int argc,char *argv[])
|
|
739 {
|
|
740 /* log in Mr. A. N. Onymous */
|
|
741 return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL,
|
|
742 (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL),
|
|
743 argc,argv);
|
|
744 }
|
|
745
|
|
746 /* Finish log in and environment initialization
|
|
747 * Accepts: passwd struct for loginpw()
|
|
748 * optional authentication user name
|
|
749 * user name (NIL for anonymous)
|
|
750 * home directory (NIL to use directory from passwd struct)
|
|
751 * argument count
|
|
752 * argument vector
|
|
753 * Returns: T if successful, NIL if error
|
|
754 */
|
|
755
|
|
756 long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
|
|
757 char *argv[])
|
|
758 {
|
|
759 struct group *gr;
|
|
760 char **t;
|
|
761 long ret = NIL;
|
|
762 if (pw && pw->pw_uid) { /* must have passwd struct for non-UID 0 */
|
|
763 /* make safe copies of user and home */
|
|
764 if (user) user = cpystr (pw->pw_name);
|
|
765 home = cpystr (home ? home : pw->pw_dir);
|
|
766 /* authorization ID .NE. authentication ID? */
|
|
767 if (user && auser && *auser && compare_cstring (auser,user)) {
|
|
768 /* scan list of mail administrators */
|
|
769 if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret)
|
|
770 if (!compare_cstring (auser,*t++))
|
|
771 ret = pw_login (pw,NIL,user,home,argc,argv);
|
|
772 syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s",
|
|
773 ret ? "Admin" : "Failed",auser,user,tcp_clienthost ());
|
|
774 }
|
|
775 else if (closedBox) { /* paranoid site, lock out other directories */
|
|
776 if (chdir (home) || chroot (home))
|
|
777 syslog (LOG_NOTICE|LOG_AUTH,
|
|
778 "Login %s failed: unable to set chroot=%.80s host=%.80s",
|
|
779 pw->pw_name,home,tcp_clienthost ());
|
|
780 else if (loginpw (pw,argc,argv)) ret = env_init (user,NIL);
|
|
781 else fatal ("Login failed after chroot");
|
|
782 }
|
|
783 /* normal login */
|
|
784 else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) &&
|
3
|
785 (ret = env_init (user,home))) chdir (myhomedir ());
|
0
|
786 fs_give ((void **) &home); /* clean up */
|
|
787 if (user) fs_give ((void **) &user);
|
|
788 }
|
|
789 endpwent (); /* in case shadow passwords in pw data */
|
|
790 return ret; /* return status */
|
|
791 }
|
|
792
|
|
793 /* Initialize environment
|
|
794 * Accepts: user name (NIL for anonymous)
|
|
795 * home directory name
|
|
796 * Returns: T, always
|
|
797 */
|
|
798
|
|
799 long env_init (char *user,char *home)
|
|
800 {
|
|
801 extern MAILSTREAM CREATEPROTO;
|
|
802 extern MAILSTREAM EMPTYPROTO;
|
|
803 struct passwd *pw;
|
|
804 struct stat sbuf;
|
|
805 char tmp[MAILTMPLEN];
|
|
806 /* don't init if blocked */
|
|
807 if (block_env_init) return LONGT;
|
|
808 if (myUserName) fatal ("env_init called twice!");
|
|
809 /* initially nothing in namespace list */
|
|
810 nslist[0] = nslist[1] = nslist[2] = NIL;
|
|
811 /* myUserName must be set before dorc() call */
|
|
812 myUserName = cpystr (user ? user : ANONYMOUSUSER);
|
|
813 /* force default prototypes to be set */
|
|
814 if (!createProto) createProto = &CREATEPROTO;
|
|
815 if (!appendProto) appendProto = &EMPTYPROTO;
|
|
816 dorc (NIL,NIL); /* do systemwide configuration */
|
|
817 if (!home) { /* closed box server */
|
|
818 /* standard user can only reference home */
|
|
819 if (user) nslist[0] = &nshome;
|
|
820 else { /* anonymous user */
|
|
821 nslist[0] = &nsblackother; /* set root */
|
|
822 anonymous = T; /* flag as anonymous */
|
|
823 }
|
|
824 myHomeDir = cpystr (""); /* home directory is root */
|
|
825 sysInbox = cpystr ("INBOX");/* make system INBOX */
|
|
826 }
|
|
827 else { /* open or black box */
|
|
828 closedBox = NIL; /* definitely not a closed box */
|
|
829 if (user) { /* remember user name and home directory */
|
|
830 if (blackBoxDir) { /* build black box directory name */
|
|
831 sprintf (tmp,"%s/%s",blackBoxDir,myUserName);
|
|
832 /* must exist */
|
|
833 if (!((!stat (home = tmp,&sbuf) && (sbuf.st_mode & S_IFDIR)) ||
|
|
834 (blackBoxDefaultHome &&
|
|
835 !stat (home = blackBoxDefaultHome,&sbuf) &&
|
|
836 (sbuf.st_mode & S_IFDIR)))) fatal ("no home");
|
|
837 sysInbox = (char *) fs_get (strlen (home) + 7);
|
|
838 /* set system INBOX */
|
|
839 sprintf (sysInbox,"%s/INBOX",home);
|
|
840 blackBox = T; /* mark that it's a black box */
|
|
841 /* mbox meaningless if black box */
|
|
842 mail_parameters (NIL,DISABLE_DRIVER,(void *) "mbox");
|
|
843 }
|
|
844 nslist[0] = &nshome; /* home namespace */
|
|
845 /* limited advertise namespaces */
|
|
846 if (limitedadvertise) nslist[2] = &nslimited;
|
|
847 else if (blackBox) { /* black box namespaces */
|
|
848 nslist[1] = &nsblackother;
|
|
849 nslist[2] = &nsshared;
|
|
850 }
|
|
851 else { /* open box namespaces */
|
|
852 nslist[1] = &nsunixother;
|
|
853 nslist[2] = advertisetheworld ? &nsworld : &nsshared;
|
|
854 }
|
|
855 }
|
|
856 else {
|
|
857 nslist[2] = &nsftp; /* anonymous user */
|
|
858 sprintf (tmp,"%s/INBOX",
|
|
859 home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL));
|
|
860 sysInbox = cpystr (tmp); /* make system INBOX */
|
|
861 anonymous = T; /* flag as anonymous */
|
|
862 }
|
|
863 myHomeDir = cpystr (home); /* set home directory */
|
|
864 }
|
|
865
|
|
866 if (allowuserconfig) { /* allow user config files */
|
|
867 dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T);
|
|
868 dorc (strcat (strcpy (tmp,myHomeDir),"/.imaprc"),NIL);
|
|
869 }
|
|
870 if (!closedBox && !noautomaticsharedns) {
|
|
871 /* #ftp namespace */
|
|
872 if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir);
|
|
873 /* #public namespace */
|
|
874 if (!publicHome && (pw = getpwnam ("imappublic")))
|
|
875 publicHome = cpystr (pw->pw_dir);
|
|
876 /* #shared namespace */
|
|
877 if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared")))
|
|
878 sharedHome = cpystr (pw->pw_dir);
|
|
879 }
|
|
880 if (!myLocalHost) mylocalhost ();
|
|
881 if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc"));
|
|
882 if (!newsActive) newsActive = cpystr (ACTIVEFILE);
|
|
883 if (!newsSpool) newsSpool = cpystr (NEWSSPOOL);
|
|
884 /* re-do open action to get flags */
|
|
885 (*createProto->dtb->open) (NIL);
|
|
886 endpwent (); /* close pw database */
|
|
887 return T;
|
|
888 }
|
|
889
|
|
890 /* Return my user name
|
|
891 * Accepts: pointer to optional flags
|
|
892 * Returns: my user name
|
|
893 */
|
|
894
|
|
895 char *myusername_full (unsigned long *flags)
|
|
896 {
|
|
897 struct passwd *pw;
|
|
898 struct stat sbuf;
|
|
899 char *s;
|
|
900 unsigned long euid;
|
|
901 char *ret = UNLOGGEDUSER;
|
|
902 /* no user name yet and not root? */
|
|
903 if (!myUserName && (euid = geteuid ())) {
|
|
904 /* yes, look up getlogin() user name or EUID */
|
|
905 if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) &&
|
|
906 (pw = getpwnam (s)) && (pw->pw_uid == euid)) ||
|
|
907 (pw = getpwuid (euid))) {
|
|
908 if (block_env_init) { /* don't env_init if blocked */
|
|
909 if (flags) *flags = MU_LOGGEDIN;
|
|
910 return pw->pw_name;
|
|
911 }
|
|
912 env_init (pw->pw_name,
|
|
913 ((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) &&
|
|
914 !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ?
|
|
915 s : pw->pw_dir);
|
|
916 }
|
|
917 else fatal ("Unable to look up user name");
|
|
918 }
|
|
919 if (myUserName) { /* logged in? */
|
|
920 if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN;
|
|
921 ret = myUserName; /* return user name */
|
|
922 }
|
|
923 else if (flags) *flags = MU_NOTLOGGEDIN;
|
|
924 return ret;
|
|
925 }
|
|
926
|
|
927
|
|
928 /* Return my local host name
|
|
929 * Returns: my local host name
|
|
930 */
|
|
931
|
|
932 char *mylocalhost ()
|
|
933 {
|
|
934 if (!myLocalHost) {
|
|
935 char *s,tmp[MAILTMPLEN];
|
|
936 char *t = "unknown";
|
|
937 tmp[0] = tmp[MAILTMPLEN-1] = '\0';
|
|
938 if (!gethostname (tmp,MAILTMPLEN-1) && tmp[0]) {
|
|
939 /* sanity check of name */
|
|
940 for (s = tmp; (*s > 0x20) && (*s < 0x7f); ++s);
|
|
941 if (!*s) t = tcp_canonical (tmp);
|
|
942 }
|
|
943 myLocalHost = cpystr (t);
|
|
944 }
|
|
945 return myLocalHost;
|
|
946 }
|
|
947
|
|
948 /* Return my home directory name
|
|
949 * Returns: my home directory name
|
|
950 */
|
|
951
|
|
952 char *myhomedir ()
|
|
953 {
|
|
954 if (!myHomeDir) myusername ();/* initialize if first time */
|
|
955 return myHomeDir ? myHomeDir : "";
|
|
956 }
|
|
957
|
|
958
|
|
959 /* Return my home mailbox name
|
|
960 * Returns: my home directory name
|
|
961 */
|
|
962
|
|
963 static char *mymailboxdir ()
|
|
964 {
|
|
965 char *home = myhomedir ();
|
|
966 /* initialize if first time */
|
|
967 if (!myMailboxDir && myHomeDir) {
|
|
968 if (mailsubdir) {
|
|
969 char tmp[MAILTMPLEN];
|
|
970 sprintf (tmp,"%s/%s",home,mailsubdir);
|
|
971 myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */
|
|
972 }
|
|
973 else myMailboxDir = cpystr (home);
|
|
974 }
|
|
975 return myMailboxDir ? myMailboxDir : "";
|
|
976 }
|
|
977
|
|
978
|
|
979 /* Return system standard INBOX
|
|
980 * Accepts: buffer string
|
|
981 */
|
|
982
|
|
983 char *sysinbox ()
|
|
984 {
|
|
985 char tmp[MAILTMPLEN];
|
|
986 if (!sysInbox) { /* initialize if first time */
|
|
987 sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
|
|
988 sysInbox = cpystr (tmp); /* system inbox is from mail spool */
|
|
989 }
|
|
990 return sysInbox;
|
|
991 }
|
|
992
|
|
993 /* Return mailbox directory name
|
|
994 * Accepts: destination buffer
|
|
995 * directory prefix
|
|
996 * name in directory
|
|
997 * Returns: file name or NIL if error
|
|
998 */
|
|
999
|
|
1000 char *mailboxdir (char *dst,char *dir,char *name)
|
|
1001 {
|
|
1002 char tmp[MAILTMPLEN];
|
|
1003 if (dir || name) { /* if either argument provided */
|
|
1004 if (dir) {
|
|
1005 if (strlen (dir) > NETMAXMBX) return NIL;
|
|
1006 strcpy (tmp,dir); /* write directory prefix */
|
|
1007 }
|
|
1008 else tmp[0] = '\0'; /* otherwise null string */
|
|
1009 if (name) {
|
|
1010 if (strlen (name) > NETMAXMBX) return NIL;
|
|
1011 strcat (tmp,name); /* write name in directory */
|
|
1012 }
|
|
1013 /* validate name, return its name */
|
|
1014 if (!mailboxfile (dst,tmp)) return NIL;
|
|
1015 }
|
|
1016 /* no arguments, wants mailbox directory */
|
|
1017 else strcpy (dst,mymailboxdir ());
|
|
1018 return dst; /* return the name */
|
|
1019 }
|
|
1020
|
|
1021 /* Return mailbox file name
|
|
1022 * Accepts: destination buffer
|
|
1023 * mailbox name
|
|
1024 * Returns: file name or empty string for driver-selected INBOX or NIL if error
|
|
1025 */
|
|
1026
|
|
1027 char *mailboxfile (char *dst,char *name)
|
|
1028 {
|
|
1029 struct passwd *pw;
|
|
1030 char *s;
|
|
1031 if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) ||
|
|
1032 ((anonymous || blackBox || restrictBox || (*name == '#')) &&
|
|
1033 (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))))
|
|
1034 dst = NIL; /* invalid name */
|
|
1035 else switch (*name) { /* determine mailbox type based upon name */
|
|
1036 case '#': /* namespace name */
|
|
1037 /* #ftp/ namespace */
|
|
1038 if (((name[1] == 'f') || (name[1] == 'F')) &&
|
|
1039 ((name[2] == 't') || (name[2] == 'T')) &&
|
|
1040 ((name[3] == 'p') || (name[3] == 'P')) &&
|
|
1041 (name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5);
|
|
1042 /* #public/ and #shared/ namespaces */
|
|
1043 else if ((((name[1] == 'p') || (name[1] == 'P')) &&
|
|
1044 ((name[2] == 'u') || (name[2] == 'U')) &&
|
|
1045 ((name[3] == 'b') || (name[3] == 'B')) &&
|
|
1046 ((name[4] == 'l') || (name[4] == 'L')) &&
|
|
1047 ((name[5] == 'i') || (name[5] == 'I')) &&
|
|
1048 ((name[6] == 'c') || (name[6] == 'C')) &&
|
|
1049 (name[7] == '/') && (s = publicHome)) ||
|
|
1050 (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&
|
|
1051 ((name[2] == 'h') || (name[2] == 'H')) &&
|
|
1052 ((name[3] == 'a') || (name[3] == 'A')) &&
|
|
1053 ((name[4] == 'r') || (name[4] == 'R')) &&
|
|
1054 ((name[5] == 'e') || (name[5] == 'E')) &&
|
|
1055 ((name[6] == 'd') || (name[6] == 'D')) &&
|
|
1056 (name[7] == '/') && (s = sharedHome)))
|
|
1057 sprintf (dst,"%s/%s",s,compare_cstring (name+8,"INBOX") ?
|
|
1058 name+8 : "INBOX");
|
|
1059 else dst = NIL; /* unknown namespace */
|
|
1060 break;
|
|
1061
|
|
1062 case '/': /* root access */
|
|
1063 if (anonymous) dst = NIL; /* anonymous forbidden to do this */
|
|
1064 else if (blackBox) { /* other user access if blackbox */
|
|
1065 if (restrictBox & RESTRICTOTHERUSER) dst = NIL;
|
|
1066 /* see if other user INBOX */
|
|
1067 else if ((s = strchr (name+1,'/')) && !compare_cstring (s+1,"INBOX")) {
|
|
1068 *s = '\0'; /* temporarily tie off string */
|
|
1069 sprintf (dst,"%s/%s/INBOX",blackBoxDir,name+1);
|
|
1070 *s = '/'; /* in case caller cares */
|
|
1071 }
|
|
1072 else sprintf (dst,"%s/%s",blackBoxDir,name+1);
|
|
1073 }
|
|
1074 else if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ()))
|
|
1075 dst = NIL; /* restricted and not access to sysinbox */
|
|
1076 else strcpy (dst,name); /* unrestricted, copy root name */
|
|
1077 break;
|
|
1078 case '~': /* other user access */
|
|
1079 /* bad syntax or anonymous can't win */
|
|
1080 if (!*++name || anonymous) dst = NIL;
|
|
1081 /* ~/ equivalent to ordinary name */
|
|
1082 else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1);
|
|
1083 /* other user forbidden if closed/restricted */
|
|
1084 else if (closedBox || (restrictBox & RESTRICTOTHERUSER)) dst = NIL;
|
|
1085 else if (blackBox) { /* black box form of other user */
|
|
1086 /* see if other user INBOX */
|
|
1087 if ((s = strchr (name,'/')) && compare_cstring (s+1,"INBOX")) {
|
|
1088 *s = '\0'; /* temporarily tie off string */
|
|
1089 sprintf (dst,"%s/%s/INBOX",blackBoxDir,name);
|
|
1090 *s = '/'; /* in case caller cares */
|
|
1091 }
|
|
1092 else sprintf (dst,"%s/%s",blackBoxDir,name);
|
|
1093 }
|
|
1094 else { /* clear box other user */
|
|
1095 /* copy user name */
|
|
1096 for (s = dst; *name && (*name != '/'); *s++ = *name++);
|
|
1097 *s++ = '\0'; /* tie off user name, look up in passwd file */
|
|
1098 if ((pw = getpwnam (dst)) && pw->pw_dir) {
|
|
1099 if (*name) name++; /* skip past the slash */
|
|
1100 /* canonicalize case of INBOX */
|
|
1101 if (!compare_cstring (name,"INBOX")) name = "INBOX";
|
|
1102 /* remove trailing / from directory */
|
|
1103 if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0';
|
|
1104 /* don't allow ~root/ if restricted root */
|
|
1105 if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL;
|
|
1106 /* build final name w/ subdir if needed */
|
|
1107 else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name);
|
|
1108 else sprintf (dst,"%s/%s",pw->pw_dir,name);
|
|
1109 }
|
|
1110 else dst = NIL; /* no such user */
|
|
1111 }
|
|
1112 break;
|
|
1113
|
|
1114 case 'I': case 'i': /* possible INBOX */
|
|
1115 if (!compare_cstring (name+1,"NBOX")) {
|
|
1116 /* if restricted, use INBOX in mailbox dir */
|
|
1117 if (anonymous || blackBox || closedBox)
|
|
1118 sprintf (dst,"%s/INBOX",mymailboxdir ());
|
|
1119 else *dst = '\0'; /* otherwise driver selects the name */
|
|
1120 break;
|
|
1121 }
|
|
1122 /* drop into to ordinary name case */
|
|
1123 default: /* ordinary name is easy */
|
|
1124 sprintf (dst,"%s/%s",mymailboxdir (),name);
|
|
1125 break;
|
|
1126 }
|
|
1127 return dst; /* return final name */
|
|
1128 }
|
|
1129
|
|
1130 /* Dot-lock file locker
|
|
1131 * Accepts: file name to lock
|
|
1132 * destination buffer for lock file name
|
|
1133 * open file description on file name to lock
|
|
1134 * Returns: T if success, NIL if failure
|
|
1135 */
|
|
1136
|
|
1137 long dotlock_lock (char *file,DOTLOCK *base,int fd)
|
|
1138 {
|
|
1139 int i = locktimeout * 60;
|
|
1140 int j,mask,retry,pi[2],po[2];
|
|
1141 char *s,tmp[MAILTMPLEN];
|
|
1142 struct stat sb;
|
|
1143 /* flush absurd file name */
|
|
1144 if (strlen (file) > 512) return NIL;
|
|
1145 /* build lock filename */
|
|
1146 sprintf (base->lock,"%s.lock",file);
|
|
1147 /* assume no pipe */
|
|
1148 base->pipei = base->pipeo = -1;
|
|
1149 do { /* make sure not symlink */
|
|
1150 if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
|
|
1151 /* time out if file older than 5 minutes */
|
|
1152 if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0;
|
|
1153 /* try to create the lock */
|
|
1154 switch (retry = crexcl (base->lock)) {
|
|
1155 case -1: /* OK to retry */
|
|
1156 if (!(i%15)) { /* time to notify? */
|
|
1157 sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
|
|
1158 file,i);
|
|
1159 MM_LOG (tmp,WARN);
|
|
1160 }
|
|
1161 sleep (1); /* wait 1 second before next try */
|
|
1162 break;
|
|
1163 case NIL: /* failure, can't retry */
|
|
1164 i = 0;
|
|
1165 break;
|
|
1166 case T: /* success, make sure others can break lock */
|
|
1167 chmod (base->lock,(int) dotlock_mode);
|
|
1168 return LONGT;
|
|
1169 }
|
|
1170 } while (i--); /* until out of retries */
|
|
1171 if (retry < 0) { /* still returning retry after locktimeout? */
|
|
1172 if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
|
|
1173 if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) {
|
|
1174 sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock",
|
|
1175 (long) (time (0) - sb.st_ctime));
|
|
1176 MM_LOG (tmp,WARN);
|
|
1177 }
|
|
1178 mask = umask (0); /* want our lock protection */
|
|
1179 unlink (base->lock); /* try to remove the old file */
|
|
1180 /* seize the lock */
|
|
1181 if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) dotlock_mode)) >= 0) {
|
|
1182 close (i); /* don't need descriptor any more */
|
|
1183 sprintf (tmp,"Mailbox %.80s lock overridden",file);
|
|
1184 MM_LOG (tmp,NIL);
|
|
1185 chmod (base->lock,(int) dotlock_mode);
|
|
1186 umask (mask); /* restore old umask */
|
|
1187 return LONGT;
|
|
1188 }
|
|
1189 umask (mask); /* restore old umask */
|
|
1190 }
|
|
1191
|
|
1192 if (fd >= 0) switch (errno) {
|
|
1193 case EACCES: /* protection failure? */
|
|
1194 MM_CRITICAL (NIL); /* go critical */
|
|
1195 if (closedBox || !lockpgm); /* can't do on closed box or disabled */
|
|
1196 else if ((*lockpgm && stat (lockpgm,&sb)) ||
|
|
1197 (!*lockpgm && stat (lockpgm = LOCKPGM1,&sb) &&
|
|
1198 stat (lockpgm = LOCKPGM2,&sb) && stat (lockpgm = LOCKPGM3,&sb)))
|
|
1199 lockpgm = NIL; /* disable if can't find lockpgm */
|
|
1200 else if (pipe (pi) >= 0) { /* make command pipes */
|
|
1201 long cf;
|
|
1202 char *argv[4],arg[20];
|
|
1203 /* if input pipes usable create output pipes */
|
|
1204 if ((pi[0] < FD_SETSIZE) && (pi[1] < FD_SETSIZE) && (pipe (po) >= 0)) {
|
|
1205 /* make sure output pipes are usable */
|
|
1206 if ((po[0] >= FD_SETSIZE) || (po[1] >= FD_SETSIZE));
|
|
1207 /* all is good, make inferior process */
|
|
1208 else if (!(j = fork ())) {
|
|
1209 if (!fork ()) { /* make grandchild so it's inherited by init */
|
|
1210 /* prepare argument vector */
|
|
1211 sprintf (arg,"%d",fd);
|
|
1212 argv[0] = lockpgm; argv[1] = arg;
|
|
1213 argv[2] = file; argv[3] = NIL;
|
|
1214 /* set parent's I/O to my O/I */
|
|
1215 dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);
|
|
1216 /* close all unnecessary descriptors */
|
|
1217 for (cf = max (20,max (max (pi[0],pi[1]),max(po[0],po[1])));
|
|
1218 cf >= 3; --cf) if (cf != fd) close (cf);
|
|
1219 /* be our own process group */
|
|
1220 setpgrp (0,getpid ());
|
|
1221 /* now run it */
|
|
1222 _exit (execv (argv[0],argv));
|
|
1223 }
|
|
1224 _exit (1); /* child is done */
|
|
1225 }
|
|
1226 else if (j > 0) { /* parent process */
|
|
1227 fd_set rfd;
|
|
1228 struct timeval tmo;
|
|
1229 FD_ZERO (&rfd);
|
|
1230 FD_SET (pi[0],&rfd);
|
|
1231 tmo.tv_sec = locktimeout * 60;
|
|
1232 grim_pid_reap (j,NIL);/* reap child; grandchild now owned by init */
|
|
1233 /* read response from locking program */
|
|
1234 if (select (pi[0]+1,&rfd,0,0,&tmo) &&
|
|
1235 (read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {
|
|
1236 /* success, record pipes */
|
|
1237 base->pipei = pi[0]; base->pipeo = po[1];
|
|
1238 /* close child's side of the pipes */
|
|
1239 close (pi[1]); close (po[0]);
|
|
1240 MM_NOCRITICAL (NIL);/* no longer critical */
|
|
1241 return LONGT;
|
|
1242 }
|
|
1243 }
|
|
1244 close (po[0]); close (po[1]);
|
|
1245 }
|
|
1246 close (pi[0]); close (pi[1]);
|
|
1247 }
|
|
1248
|
|
1249 MM_NOCRITICAL (NIL); /* no longer critical */
|
|
1250 /* find directory/file delimiter */
|
|
1251 if (s = strrchr (base->lock,'/')) {
|
|
1252 *s = '\0'; /* tie off at directory */
|
|
1253 sprintf(tmp, /* generate default message */
|
|
1254 "Mailbox vulnerable - directory %.80s must have 1777 protection",
|
|
1255 base->lock);
|
|
1256 /* definitely not 1777 if can't stat */
|
|
1257 mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777);
|
|
1258 *s = '/'; /* restore lock name */
|
|
1259 if (mask != 1777) { /* default warning if not 1777 */
|
|
1260 if (!disableLockWarning) MM_LOG (tmp,WARN);
|
|
1261 break;
|
|
1262 }
|
|
1263 }
|
|
1264 default:
|
|
1265 sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",
|
|
1266 base->lock,strerror (errno));
|
|
1267 if (!disableLockWarning) MM_LOG (tmp,WARN);
|
|
1268 break;
|
|
1269 }
|
|
1270 base->lock[0] = '\0'; /* don't use lock files */
|
|
1271 return NIL;
|
|
1272 }
|
|
1273
|
|
1274 /* Dot-lock file unlocker
|
|
1275 * Accepts: lock file name
|
|
1276 * Returns: T if success, NIL if failure
|
|
1277 */
|
|
1278
|
|
1279 long dotlock_unlock (DOTLOCK *base)
|
|
1280 {
|
|
1281 long ret = LONGT;
|
|
1282 if (base && base->lock[0]) {
|
|
1283 if (base->pipei >= 0) { /* if running through a pipe unlocker */
|
|
1284 ret = (write (base->pipeo,"+",1) == 1);
|
|
1285 /* nuke the pipes */
|
|
1286 close (base->pipei); close (base->pipeo);
|
|
1287 }
|
|
1288 else ret = !unlink (base->lock);
|
|
1289 }
|
|
1290 return ret;
|
|
1291 }
|
|
1292
|
|
1293 /* Lock file name
|
|
1294 * Accepts: scratch buffer
|
|
1295 * file name
|
|
1296 * type of locking operation (LOCK_SH or LOCK_EX)
|
|
1297 * pointer to return PID of locker
|
|
1298 * Returns: file descriptor of lock or negative if error
|
|
1299 */
|
|
1300
|
|
1301 int lockname (char *lock,char *fname,int op,long *pid)
|
|
1302 {
|
|
1303 struct stat sbuf;
|
|
1304 *pid = 0; /* no locker PID */
|
|
1305 return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);
|
|
1306 }
|
|
1307
|
|
1308
|
|
1309 /* Lock file descriptor
|
|
1310 * Accepts: file descriptor
|
|
1311 * lock file name buffer
|
|
1312 * type of locking operation (LOCK_SH or LOCK_EX)
|
|
1313 * Returns: file descriptor of lock or negative if error
|
|
1314 */
|
|
1315
|
|
1316 int lockfd (int fd,char *lock,int op)
|
|
1317 {
|
|
1318 struct stat sbuf;
|
|
1319 return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);
|
|
1320 }
|
|
1321
|
|
1322 /* Lock file name worker
|
|
1323 * Accepts: lock file name
|
|
1324 * pointer to stat() buffer
|
|
1325 * type of locking operation (LOCK_SH or LOCK_EX)
|
|
1326 * pointer to return PID of locker
|
|
1327 * Returns: file descriptor of lock or negative if error
|
|
1328 */
|
|
1329
|
|
1330 int lock_work (char *lock,void *sb,int op,long *pid)
|
|
1331 {
|
|
1332 struct stat lsb,fsb;
|
|
1333 struct stat *sbuf = (struct stat *) sb;
|
|
1334 char tmp[MAILTMPLEN];
|
|
1335 long i;
|
|
1336 int fd;
|
|
1337 int mask = umask (0);
|
|
1338 if (pid) *pid = 0; /* initialize return PID */
|
|
1339 /* make temporary lock file name */
|
|
1340 sprintf (lock,"%s/.%lx.%lx",closedBox ? "" : tmpdir,
|
|
1341 (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino);
|
|
1342 while (T) { /* until get a good lock */
|
|
1343 do switch ((int) chk_notsymlink (lock,&lsb)) {
|
|
1344 case 1: /* exists just once */
|
|
1345 if (((fd = open (lock,O_RDWR,shlock_mode)) >= 0) ||
|
|
1346 (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;
|
|
1347 case -1: /* name doesn't exist */
|
|
1348 fd = open (lock,O_RDWR|O_CREAT|O_EXCL,shlock_mode);
|
|
1349 break;
|
|
1350 default: /* multiple hard links */
|
|
1351 MM_LOG ("hard link to lock name",ERROR);
|
|
1352 syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);
|
|
1353 case 0: /* symlink (already did syslog) */
|
|
1354 umask (mask); /* restore old mask */
|
|
1355 return -1; /* fail: no lock file */
|
|
1356 } while ((fd < 0) && (errno == EEXIST));
|
|
1357 if (fd < 0) { /* failed to get file descriptor */
|
|
1358 syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,
|
|
1359 strerror (errno));
|
|
1360 if (!closedBox) { /* more explicit snarl for bad configuration */
|
|
1361 if (stat (tmpdir,&lsb))
|
|
1362 syslog (LOG_CRIT,"SYSTEM ERROR: no %s: %s",tmpdir,strerror (errno));
|
|
1363 else if ((lsb.st_mode & 01777) != 01777) {
|
|
1364 sprintf (tmp,"Can't lock for write: %.80s must have 1777 protection",
|
|
1365 tmpdir);
|
|
1366 MM_LOG (tmp,WARN);
|
|
1367 }
|
|
1368 }
|
|
1369 umask (mask); /* restore old mask */
|
|
1370 return -1; /* fail: can't open lock file */
|
|
1371 }
|
|
1372
|
|
1373 /* non-blocking form */
|
|
1374 if (op & LOCK_NB) i = flock (fd,op);
|
|
1375 else { /* blocking form */
|
|
1376 (*mailblocknotify) (BLOCK_FILELOCK,NIL);
|
|
1377 i = flock (fd,op);
|
|
1378 (*mailblocknotify) (BLOCK_NONE,NIL);
|
|
1379 }
|
|
1380 if (i) { /* failed, get other process' PID */
|
|
1381 if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&
|
|
1382 (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))
|
|
1383 *pid = i;
|
|
1384 close (fd); /* failed, give up on lock */
|
|
1385 umask (mask); /* restore old mask */
|
|
1386 return -1; /* fail: can't lock */
|
|
1387 }
|
|
1388 /* make sure this lock is good for us */
|
|
1389 if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&
|
|
1390 !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&
|
|
1391 (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;
|
|
1392 close (fd); /* lock not right, drop fd and try again */
|
|
1393 }
|
|
1394 chmod (lock,shlock_mode); /* make sure mode OK (don't use fchmod()) */
|
|
1395 umask (mask); /* restore old mask */
|
|
1396 return fd; /* success */
|
|
1397 }
|
|
1398
|
|
1399 /* Check to make sure not a symlink
|
|
1400 * Accepts: file name
|
|
1401 * stat buffer
|
|
1402 * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links
|
|
1403 */
|
|
1404
|
|
1405 long chk_notsymlink (char *name,void *sb)
|
|
1406 {
|
|
1407 struct stat *sbuf = (struct stat *) sb;
|
|
1408 /* name exists? */
|
|
1409 if (lstat (name,sbuf)) return -1;
|
|
1410 /* forbid symbolic link */
|
|
1411 if ((sbuf->st_mode & S_IFMT) == S_IFLNK) {
|
|
1412 MM_LOG ("symbolic link on lock name",ERROR);
|
|
1413 syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s",
|
|
1414 name);
|
|
1415 return NIL;
|
|
1416 }
|
|
1417 return (long) sbuf->st_nlink; /* return number of hard links */
|
|
1418 }
|
|
1419
|
|
1420
|
|
1421 /* Unlock file descriptor
|
|
1422 * Accepts: file descriptor
|
|
1423 * lock file name from lockfd()
|
|
1424 */
|
|
1425
|
|
1426 void unlockfd (int fd,char *lock)
|
|
1427 {
|
|
1428 /* delete the file if no sharers */
|
|
1429 if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock);
|
|
1430 flock (fd,LOCK_UN); /* unlock it */
|
|
1431 close (fd); /* close it */
|
|
1432 }
|
|
1433
|
|
1434 /* Set proper file protection for mailbox
|
|
1435 * Accepts: mailbox name
|
|
1436 * actual file path name
|
|
1437 * Returns: T, always
|
|
1438 */
|
|
1439
|
|
1440 long set_mbx_protections (char *mailbox,char *path)
|
|
1441 {
|
|
1442 struct stat sbuf;
|
|
1443 int mode = (int) mbx_protection;
|
|
1444 if (*mailbox == '#') { /* possible namespace? */
|
|
1445 if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
|
|
1446 ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
|
|
1447 ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
|
|
1448 (mailbox[4] == '/')) mode = (int) ftp_protection;
|
|
1449 else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
|
|
1450 ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
|
|
1451 ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
|
|
1452 ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
|
|
1453 ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
|
|
1454 ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
|
|
1455 (mailbox[7] == '/')) mode = (int) public_protection;
|
|
1456 else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
|
|
1457 ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
|
|
1458 ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
|
|
1459 ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
|
|
1460 ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
|
|
1461 ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
|
|
1462 (mailbox[7] == '/')) mode = (int) shared_protection;
|
|
1463 }
|
|
1464 /* if a directory */
|
|
1465 if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
|
|
1466 /* set owner search if allow read or write */
|
|
1467 if (mode & 0600) mode |= 0100;
|
|
1468 if (mode & 060) mode |= 010;/* set group search if allow read or write */
|
|
1469 if (mode & 06) mode |= 01; /* set world search if allow read or write */
|
|
1470 /* preserve directory SGID bit */
|
|
1471 if (sbuf.st_mode & S_ISGID) mode |= S_ISGID;
|
|
1472 }
|
|
1473 chmod (path,mode); /* set the new protection, ignore failure */
|
|
1474 return LONGT;
|
|
1475 }
|
|
1476
|
|
1477 /* Get proper directory protection
|
|
1478 * Accepts: mailbox name
|
|
1479 * Returns: directory mode, always
|
|
1480 */
|
|
1481
|
|
1482 long get_dir_protection (char *mailbox)
|
|
1483 {
|
|
1484 if (*mailbox == '#') { /* possible namespace? */
|
|
1485 if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
|
|
1486 ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
|
|
1487 ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
|
|
1488 (mailbox[4] == '/')) return ftp_dir_protection;
|
|
1489 else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
|
|
1490 ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
|
|
1491 ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
|
|
1492 ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
|
|
1493 ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
|
|
1494 ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
|
|
1495 (mailbox[7] == '/')) return public_dir_protection;
|
|
1496 else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
|
|
1497 ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
|
|
1498 ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
|
|
1499 ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
|
|
1500 ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
|
|
1501 ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
|
|
1502 (mailbox[7] == '/')) return shared_dir_protection;
|
|
1503 }
|
|
1504 return dir_protection;
|
|
1505 }
|
|
1506
|
|
1507 /* Determine default prototype stream to user
|
|
1508 * Accepts: type (NIL for create, T for append)
|
|
1509 * Returns: default prototype stream
|
|
1510 */
|
|
1511
|
|
1512 MAILSTREAM *default_proto (long type)
|
|
1513 {
|
|
1514 myusername (); /* make sure initialized */
|
|
1515 /* return default driver's prototype */
|
|
1516 return type ? appendProto : createProto;
|
|
1517 }
|
|
1518
|
|
1519
|
|
1520 /* Set up user flags for stream
|
|
1521 * Accepts: MAIL stream
|
|
1522 * Returns: MAIL stream with user flags set up
|
|
1523 */
|
|
1524
|
|
1525 MAILSTREAM *user_flags (MAILSTREAM *stream)
|
|
1526 {
|
|
1527 int i;
|
|
1528 myusername (); /* make sure initialized */
|
|
1529 for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i)
|
|
1530 if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]);
|
|
1531 return stream;
|
|
1532 }
|
|
1533
|
|
1534
|
|
1535 /* Return nth user flag
|
|
1536 * Accepts: user flag number
|
|
1537 * Returns: flag
|
|
1538 */
|
|
1539
|
|
1540 char *default_user_flag (unsigned long i)
|
|
1541 {
|
|
1542 myusername (); /* make sure initialized */
|
|
1543 return userFlags[i];
|
|
1544 }
|
|
1545
|
|
1546 /* Process rc file
|
|
1547 * Accepts: file name
|
|
1548 * .mminit flag
|
|
1549 * Don't use this feature.
|
|
1550 */
|
|
1551
|
|
1552 void dorc (char *file,long flag)
|
|
1553 {
|
|
1554 int i;
|
|
1555 char *s,*t,*k,*r,tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
|
|
1556 extern MAILSTREAM CREATEPROTO;
|
|
1557 extern MAILSTREAM EMPTYPROTO;
|
|
1558 DRIVER *d;
|
|
1559 FILE *f;
|
|
1560 if ((f = fopen (file ? file : SYSCONFIG,"r")) &&
|
|
1561 (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))) do {
|
|
1562 *t++ = '\0'; /* tie off line, find second space */
|
|
1563 if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
|
|
1564 *k++ = '\0'; /* tie off two words */
|
|
1565 if (!compare_cstring (s,"set keywords") && !userFlags[0]) {
|
|
1566 /* yes, get first keyword */
|
|
1567 k = strtok_r (k,", ",&r);
|
|
1568 /* copy keyword list */
|
|
1569 for (i = 0; k && i < NUSERFLAGS; ++i) if (strlen (k) <= MAXUSERFLAG) {
|
|
1570 if (userFlags[i]) fs_give ((void **) &userFlags[i]);
|
|
1571 userFlags[i] = cpystr (k);
|
|
1572 k = strtok_r (NIL,", ",&r);
|
|
1573 }
|
|
1574 if (flag) break; /* found "set keywords" in .mminit */
|
|
1575 }
|
|
1576
|
|
1577 else if (!flag) { /* none of these valid in .mminit */
|
|
1578 if (myUserName) { /* only valid if logged in */
|
|
1579 if (!compare_cstring (s,"set new-mailbox-format") ||
|
|
1580 !compare_cstring (s,"set new-folder-format")) {
|
|
1581 if (!compare_cstring (k,"same-as-inbox")) {
|
|
1582 if (d = mail_valid (NIL,"INBOX",NIL)) {
|
|
1583 if (!compare_cstring (d->name,"mbox"))
|
|
1584 d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,
|
|
1585 (void *) "unix");
|
|
1586 else if (!compare_cstring (d->name,"dummy")) d = NIL;
|
|
1587 }
|
|
1588 createProto = d ? ((*d->open) (NIL)) : &CREATEPROTO;
|
|
1589 }
|
|
1590 else if (!compare_cstring (k,"system-standard"))
|
|
1591 createProto = &CREATEPROTO;
|
|
1592 else { /* canonicalize mbox to unix */
|
|
1593 if (!compare_cstring (k,"mbox")) k = "unix";
|
|
1594 /* see if a driver name */
|
|
1595 if (d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,(void *) k))
|
|
1596 createProto = (*d->open) (NIL);
|
|
1597 else { /* duh... */
|
|
1598 sprintf (tmpx,"Unknown new mailbox format in %s: %s",
|
|
1599 file ? file : SYSCONFIG,k);
|
|
1600 MM_LOG (tmpx,WARN);
|
|
1601 }
|
|
1602 }
|
|
1603 }
|
|
1604 if (!compare_cstring (s,"set empty-mailbox-format") ||
|
|
1605 !compare_cstring (s,"set empty-folder-format")) {
|
|
1606 if (!compare_cstring (k,"invalid")) appendProto = NIL;
|
|
1607 else if (!compare_cstring (k,"same-as-inbox"))
|
|
1608 appendProto = ((d = mail_valid (NIL,"INBOX",NIL)) &&
|
|
1609 compare_cstring (d->name,"dummy")) ?
|
|
1610 ((*d->open) (NIL)) : &EMPTYPROTO;
|
|
1611 else if (!compare_cstring (k,"system-standard"))
|
|
1612 appendProto = &EMPTYPROTO;
|
|
1613 else { /* see if a driver name */
|
|
1614 for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
|
|
1615 d && compare_cstring (d->name,k); d = d->next);
|
|
1616 if (d) appendProto = (*d->open) (NIL);
|
|
1617 else { /* duh... */
|
|
1618 sprintf (tmpx,"Unknown empty mailbox format in %s: %s",
|
|
1619 file ? file : SYSCONFIG,k);
|
|
1620 MM_LOG (tmpx,WARN);
|
|
1621 }
|
|
1622 }
|
|
1623 }
|
|
1624 }
|
|
1625
|
|
1626 if (!compare_cstring (s,"set local-host")) {
|
|
1627 fs_give ((void **) &myLocalHost);
|
|
1628 myLocalHost = cpystr (k);
|
|
1629 }
|
|
1630 else if (!compare_cstring (s,"set news-active-file")) {
|
|
1631 fs_give ((void **) &newsActive);
|
|
1632 newsActive = cpystr (k);
|
|
1633 }
|
|
1634 else if (!compare_cstring (s,"set news-spool-directory")) {
|
|
1635 fs_give ((void **) &newsSpool);
|
|
1636 newsSpool = cpystr (k);
|
|
1637 }
|
|
1638 else if (!compare_cstring (s,"set mh-path"))
|
|
1639 mail_parameters (NIL,SET_MHPATH,(void *) k);
|
|
1640 else if (!compare_cstring (s,"set mh-allow-inbox"))
|
|
1641 mail_parameters (NIL,SET_MHALLOWINBOX,(void *) atol (k));
|
|
1642 else if (!compare_cstring (s,"set news-state-file")) {
|
|
1643 fs_give ((void **) &myNewsrc);
|
|
1644 myNewsrc = cpystr (k);
|
|
1645 }
|
|
1646 else if (!compare_cstring (s,"set ftp-export-directory")) {
|
|
1647 fs_give ((void **) &ftpHome);
|
|
1648 ftpHome = cpystr (k);
|
|
1649 }
|
|
1650 else if (!compare_cstring (s,"set public-home-directory")) {
|
|
1651 fs_give ((void **) &publicHome);
|
|
1652 publicHome = cpystr (k);
|
|
1653 }
|
|
1654 else if (!compare_cstring (s,"set shared-home-directory")) {
|
|
1655 fs_give ((void **) &sharedHome);
|
|
1656 sharedHome = cpystr (k);
|
|
1657 }
|
|
1658 else if (!compare_cstring (s,"set system-inbox")) {
|
|
1659 fs_give ((void **) &sysInbox);
|
|
1660 sysInbox = cpystr (k);
|
|
1661 }
|
|
1662 else if (!compare_cstring (s,"set mail-subdirectory")) {
|
|
1663 fs_give ((void **) &mailsubdir);
|
|
1664 mailsubdir = cpystr (k);
|
|
1665 }
|
|
1666 else if (!compare_cstring (s,"set from-widget"))
|
|
1667 mail_parameters (NIL,SET_FROMWIDGET,
|
|
1668 compare_cstring (k,"header-only") ?
|
|
1669 VOIDT : NIL);
|
|
1670
|
|
1671 else if (!compare_cstring (s,"set rsh-command"))
|
|
1672 mail_parameters (NIL,SET_RSHCOMMAND,(void *) k);
|
|
1673 else if (!compare_cstring (s,"set rsh-path"))
|
|
1674 mail_parameters (NIL,SET_RSHPATH,(void *) k);
|
|
1675 else if (!compare_cstring (s,"set ssh-command"))
|
|
1676 mail_parameters (NIL,SET_SSHCOMMAND,(void *) k);
|
|
1677 else if (!compare_cstring (s,"set ssh-path"))
|
|
1678 mail_parameters (NIL,SET_SSHPATH,(void *) k);
|
|
1679 else if (!compare_cstring (s,"set tcp-open-timeout"))
|
|
1680 mail_parameters (NIL,SET_OPENTIMEOUT,(void *) atol (k));
|
|
1681 else if (!compare_cstring (s,"set tcp-read-timeout"))
|
|
1682 mail_parameters (NIL,SET_READTIMEOUT,(void *) atol (k));
|
|
1683 else if (!compare_cstring (s,"set tcp-write-timeout"))
|
|
1684 mail_parameters (NIL,SET_WRITETIMEOUT,(void *) atol (k));
|
|
1685 else if (!compare_cstring (s,"set rsh-timeout"))
|
|
1686 mail_parameters (NIL,SET_RSHTIMEOUT,(void *) atol (k));
|
|
1687 else if (!compare_cstring (s,"set ssh-timeout"))
|
|
1688 mail_parameters (NIL,SET_SSHTIMEOUT,(void *) atol (k));
|
|
1689 else if (!compare_cstring (s,"set maximum-login-trials"))
|
|
1690 mail_parameters (NIL,SET_MAXLOGINTRIALS,(void *) atol (k));
|
|
1691 else if (!compare_cstring (s,"set lookahead"))
|
|
1692 mail_parameters (NIL,SET_LOOKAHEAD,(void *) atol (k));
|
|
1693 else if (!compare_cstring (s,"set prefetch"))
|
|
1694 mail_parameters (NIL,SET_PREFETCH,(void *) atol (k));
|
|
1695 else if (!compare_cstring (s,"set close-on-error"))
|
|
1696 mail_parameters (NIL,SET_CLOSEONERROR,(void *) atol (k));
|
|
1697 else if (!compare_cstring (s,"set imap-port"))
|
|
1698 mail_parameters (NIL,SET_IMAPPORT,(void *) atol (k));
|
|
1699 else if (!compare_cstring (s,"set pop3-port"))
|
|
1700 mail_parameters (NIL,SET_POP3PORT,(void *) atol (k));
|
|
1701 else if (!compare_cstring (s,"set uid-lookahead"))
|
|
1702 mail_parameters (NIL,SET_UIDLOOKAHEAD,(void *) atol (k));
|
|
1703 else if (!compare_cstring (s,"set try-ssl-first"))
|
|
1704 mail_parameters (NIL,SET_TRYSSLFIRST,(void *) atol (k));
|
|
1705
|
|
1706 else if (!compare_cstring (s,"set mailbox-protection"))
|
|
1707 mbx_protection = atol (k);
|
|
1708 else if (!compare_cstring (s,"set directory-protection"))
|
|
1709 dir_protection = atol (k);
|
|
1710 else if (!compare_cstring (s,"set lock-protection"))
|
|
1711 dotlock_mode = atol (k);
|
|
1712 else if (!compare_cstring (s,"set ftp-protection"))
|
|
1713 ftp_protection = atol (k);
|
|
1714 else if (!compare_cstring (s,"set public-protection"))
|
|
1715 public_protection = atol (k);
|
|
1716 else if (!compare_cstring (s,"set shared-protection"))
|
|
1717 shared_protection = atol (k);
|
|
1718 else if (!compare_cstring (s,"set ftp-directory-protection"))
|
|
1719 ftp_dir_protection = atol (k);
|
|
1720 else if (!compare_cstring (s,"set public-directory-protection"))
|
|
1721 public_dir_protection = atol (k);
|
|
1722 else if (!compare_cstring (s,"set shared-directory-protection"))
|
|
1723 shared_dir_protection = atol (k);
|
|
1724 else if (!compare_cstring (s,"set dot-lock-file-timeout"))
|
|
1725 locktimeout = atoi (k);
|
|
1726 else if (!compare_cstring (s,"set disable-fcntl-locking"))
|
|
1727 fcntlhangbug = atoi (k);
|
|
1728 else if (!compare_cstring (s,"set disable-lock-warning"))
|
|
1729 disableLockWarning = atoi (k);
|
|
1730 else if (!compare_cstring (s,"set disable-unix-UIDs-and-keywords"))
|
|
1731 has_no_life = atoi (k);
|
|
1732 else if (!compare_cstring (s,"set hide-dot-files"))
|
|
1733 hideDotFiles = atoi (k);
|
|
1734 else if (!compare_cstring (s,"set list-maximum-level"))
|
|
1735 list_max_level = atol (k);
|
|
1736 else if (!compare_cstring (s,"set trust-dns"))
|
|
1737 mail_parameters (NIL,SET_TRUSTDNS,(void *) atol (k));
|
|
1738 else if (!compare_cstring (s,"set sasl-uses-ptr-name"))
|
|
1739 mail_parameters (NIL,SET_SASLUSESPTRNAME,(void *) atol (k));
|
|
1740 else if (!compare_cstring (s,"set network-filesystem-stat-bug"))
|
|
1741 netfsstatbug = atoi (k);
|
|
1742 else if (!compare_cstring (s,"set nntp-range"))
|
|
1743 mail_parameters (NIL,SET_NNTPRANGE,(void *) atol (k));
|
|
1744
|
|
1745 else if (!file) { /* only allowed in system init */
|
|
1746 if (!compare_cstring (s,"set black-box-directory") &&
|
|
1747 !blackBoxDir) blackBoxDir = cpystr (k);
|
|
1748 else if (!compare_cstring(s,"set black-box-default-home-directory")&&
|
|
1749 blackBoxDir && !blackBoxDefaultHome)
|
|
1750 blackBoxDefaultHome = cpystr (k);
|
|
1751 else if (!compare_cstring (s,"set anonymous-home-directory") &&
|
|
1752 !anonymousHome) anonymousHome = cpystr (k);
|
|
1753 /* It's tempting to allow setting the CA path
|
|
1754 * in a user init. However, that opens up a
|
|
1755 * vector of attack big enough to drive a
|
|
1756 * truck through... Resist the temptation.
|
|
1757 */
|
|
1758 else if (!compare_cstring (s,"set CA-certificate-path"))
|
|
1759 sslCApath = cpystr (k);
|
|
1760 else if (!compare_cstring (s,"set disable-plaintext"))
|
|
1761 disablePlaintext = atoi (k);
|
|
1762 else if (!compare_cstring (s,"set allowed-login-attempts"))
|
|
1763 logtry = atoi (k);
|
|
1764 else if (!compare_cstring (s,"set chroot-server"))
|
|
1765 closedBox = atoi (k);
|
|
1766 else if (!compare_cstring (s,"set restrict-mailbox-access"))
|
|
1767 for (k = strtok_r (k,", ",&r); k; k = strtok_r (NIL,", ",&r)) {
|
|
1768 if (!compare_cstring (k,"root")) restrictBox |= RESTRICTROOT;
|
|
1769 else if (!compare_cstring (k,"otherusers"))
|
|
1770 restrictBox |= RESTRICTOTHERUSER;
|
|
1771 else if (!compare_cstring (k,"all")) restrictBox = -1;
|
|
1772 }
|
|
1773 else if (!compare_cstring (s,"set advertise-the-world"))
|
|
1774 advertisetheworld = atoi (k);
|
|
1775 else if (!compare_cstring (s,"set limited-advertise"))
|
|
1776 limitedadvertise = atoi (k);
|
|
1777 else if (!compare_cstring
|
|
1778 (s,"set disable-automatic-shared-namespaces"))
|
|
1779 noautomaticsharedns = atoi (k);
|
|
1780 else if (!compare_cstring (s,"set allow-user-config"))
|
|
1781 allowuserconfig = atoi (k);
|
|
1782 else if (!compare_cstring (s,"set allow-reverse-dns"))
|
|
1783 mail_parameters (NIL,SET_ALLOWREVERSEDNS,(void *) atol (k));
|
|
1784 else if (!compare_cstring (s,"set k5-cp-uses-service-name"))
|
|
1785 kerb_cp_svr_name = atoi (k);
|
|
1786 /* must appear in file after any
|
|
1787 * "set disable-plaintext" command! */
|
|
1788 else if (!compare_cstring (s,"set plaintext-allowed-clients")) {
|
|
1789 for (k = strtok_r (k,", ",&r); k && !tcp_isclienthost (k);
|
|
1790 k = strtok_r (NIL,", ",&r));
|
|
1791 if (k) disablePlaintext = 0;
|
|
1792 }
|
|
1793 }
|
|
1794 }
|
|
1795 }
|
|
1796 } while ((s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n')));
|
|
1797 if (f) fclose (f); /* flush the file */
|
|
1798 }
|
|
1799
|
|
1800 /* INBOX create function for tmail/dmail use only
|
|
1801 * Accepts: mail stream
|
|
1802 * path name buffer, preloaded with driver-dependent path
|
|
1803 * Returns: T on success, NIL on failure
|
|
1804 *
|
|
1805 * This routine is evil and a truly incredible kludge. It is private for
|
|
1806 * tmail/dmail and is not supported for any other application.
|
|
1807 */
|
|
1808
|
|
1809 long path_create (MAILSTREAM *stream,char *path)
|
|
1810 {
|
|
1811 long ret;
|
|
1812 short rsave = restrictBox;
|
|
1813 restrictBox = NIL; /* can't restrict */
|
|
1814 if (blackBox) { /* if black box */
|
|
1815 /* toss out driver dependent names */
|
3
|
1816 sprintf (path,"%s/INBOX",mymailboxdir ());
|
0
|
1817 blackBox = NIL; /* well that's evil - evil is going on */
|
|
1818 ret = mail_create (stream,path);
|
|
1819 blackBox = T; /* restore the box */
|
|
1820 }
|
|
1821 /* easy thing otherwise */
|
|
1822 else ret = mail_create (stream,path);
|
|
1823 restrictBox = rsave; /* restore restrictions */
|
|
1824 return ret;
|
|
1825 }
|
|
1826
|
|
1827 /* Default block notify routine
|
|
1828 * Accepts: reason for calling
|
|
1829 * data
|
|
1830 * Returns: data
|
|
1831 */
|
|
1832
|
|
1833 void *mm_blocknotify (int reason,void *data)
|
|
1834 {
|
|
1835 void *ret = data;
|
|
1836 switch (reason) {
|
|
1837 case BLOCK_SENSITIVE: /* entering sensitive code */
|
|
1838 ret = (void *) (unsigned long) alarm (0);
|
|
1839 break;
|
|
1840 case BLOCK_NONSENSITIVE: /* exiting sensitive code */
|
|
1841 if ((unsigned long) data) alarm ((unsigned long) data);
|
|
1842 break;
|
|
1843 default: /* ignore all other reasons */
|
|
1844 break;
|
|
1845 }
|
|
1846 return ret;
|
|
1847 }
|