rev |
line source |
yuuji@0
|
1 /* ========================================================================
|
yuuji@0
|
2 * Copyright 1988-2008 University of Washington
|
yuuji@0
|
3 *
|
yuuji@0
|
4 * Licensed under the Apache License, Version 2.0 (the "License");
|
yuuji@0
|
5 * you may not use this file except in compliance with the License.
|
yuuji@0
|
6 * You may obtain a copy of the License at
|
yuuji@0
|
7 *
|
yuuji@0
|
8 * http://www.apache.org/licenses/LICENSE-2.0
|
yuuji@0
|
9 *
|
yuuji@0
|
10 *
|
yuuji@0
|
11 * ========================================================================
|
yuuji@0
|
12 */
|
yuuji@0
|
13
|
yuuji@0
|
14 /*
|
yuuji@0
|
15 * Program: NT environment routines
|
yuuji@0
|
16 *
|
yuuji@0
|
17 * Author: Mark Crispin
|
yuuji@0
|
18 * UW Technology
|
yuuji@0
|
19 * University of Washington
|
yuuji@0
|
20 * Seattle, WA 98195
|
yuuji@0
|
21 * Internet: MRC@Washington.EDU
|
yuuji@0
|
22 *
|
yuuji@0
|
23 * Date: 1 August 1988
|
yuuji@0
|
24 * Last Edited: 15 February 2008
|
yuuji@0
|
25 */
|
yuuji@0
|
26
|
yuuji@0
|
27 static char *myUserName = NIL; /* user name */
|
yuuji@0
|
28 static char *myLocalHost = NIL; /* local host name */
|
yuuji@0
|
29 static char *myHomeDir = NIL; /* home directory name */
|
yuuji@0
|
30 static char *myNewsrc = NIL; /* newsrc file name */
|
yuuji@0
|
31 static char *sysInbox = NIL; /* system inbox name */
|
yuuji@0
|
32 static long list_max_level = 5; /* maximum level of list recursion */
|
yuuji@0
|
33 /* block environment init */
|
yuuji@0
|
34 static short block_env_init = NIL;
|
yuuji@0
|
35 static short no822tztext = NIL; /* disable RFC [2]822 timezone text */
|
yuuji@0
|
36 /* home namespace */
|
yuuji@0
|
37 static NAMESPACE nshome = {"",'\\',NIL,NIL};
|
yuuji@0
|
38 /* UNIX other user namespace */
|
yuuji@0
|
39 static NAMESPACE nsother = {"#user.",'\\',NIL,NIL};
|
yuuji@0
|
40 /* namespace list */
|
yuuji@0
|
41 static NAMESPACE *nslist[3] = {&nshome,&nsother,NIL};
|
yuuji@0
|
42 static long alarm_countdown = 0;/* alarm count down */
|
yuuji@0
|
43 static void (*alarm_rang) (); /* alarm interrupt function */
|
yuuji@0
|
44 static unsigned int rndm = 0; /* initial `random' number */
|
yuuji@0
|
45 static int server_nli = 0; /* server and not logged in */
|
yuuji@0
|
46 static int logtry = 3; /* number of login tries */
|
yuuji@0
|
47 /* block notification */
|
yuuji@0
|
48 static blocknotify_t mailblocknotify = mm_blocknotify;
|
yuuji@0
|
49 /* callback to get username */
|
yuuji@0
|
50 static userprompt_t mailusername = NIL;
|
yuuji@0
|
51 static long is_nt = -1; /* T if NT, NIL if not NT, -1 unknown */
|
yuuji@0
|
52 static HINSTANCE netapi = NIL;
|
yuuji@0
|
53 typedef NET_API_STATUS (CALLBACK *GETINFO) (LPCWSTR,LPCWSTR,DWORD,LPBYTE *);
|
yuuji@0
|
54 static GETINFO getinfo = NIL;
|
yuuji@0
|
55
|
yuuji@0
|
56 #include "write.c" /* include safe writing routines */
|
yuuji@0
|
57 #include "pmatch.c" /* include wildcard pattern matcher */
|
yuuji@0
|
58
|
yuuji@0
|
59
|
yuuji@0
|
60 /* Get all authenticators */
|
yuuji@0
|
61
|
yuuji@0
|
62 #include "auths.c"
|
yuuji@0
|
63
|
yuuji@0
|
64 /* Environment manipulate parameters
|
yuuji@0
|
65 * Accepts: function code
|
yuuji@0
|
66 * function-dependent value
|
yuuji@0
|
67 * Returns: function-dependent return value
|
yuuji@0
|
68 */
|
yuuji@0
|
69
|
yuuji@0
|
70 void *env_parameters (long function,void *value)
|
yuuji@0
|
71 {
|
yuuji@0
|
72 void *ret = NIL;
|
yuuji@0
|
73 switch ((int) function) {
|
yuuji@0
|
74 case GET_NAMESPACE:
|
yuuji@0
|
75 ret = (void *) nslist;
|
yuuji@0
|
76 break;
|
yuuji@0
|
77 case SET_USERPROMPT :
|
yuuji@0
|
78 mailusername = (userprompt_t) value;
|
yuuji@0
|
79 case GET_USERPROMPT :
|
yuuji@0
|
80 ret = (void *) mailusername;
|
yuuji@0
|
81 break;
|
yuuji@0
|
82 case SET_HOMEDIR:
|
yuuji@0
|
83 if (myHomeDir) fs_give ((void **) &myHomeDir);
|
yuuji@0
|
84 myHomeDir = cpystr ((char *) value);
|
yuuji@0
|
85 case GET_HOMEDIR:
|
yuuji@0
|
86 ret = (void *) myHomeDir;
|
yuuji@0
|
87 break;
|
yuuji@0
|
88 case SET_LOCALHOST:
|
yuuji@0
|
89 myLocalHost = cpystr ((char *) value);
|
yuuji@0
|
90 case GET_LOCALHOST:
|
yuuji@0
|
91 if (myLocalHost) fs_give ((void **) &myLocalHost);
|
yuuji@0
|
92 ret = (void *) myLocalHost;
|
yuuji@0
|
93 break;
|
yuuji@0
|
94 case SET_NEWSRC:
|
yuuji@0
|
95 if (myNewsrc) fs_give ((void **) &myNewsrc);
|
yuuji@0
|
96 myNewsrc = cpystr ((char *) value);
|
yuuji@0
|
97 case GET_NEWSRC:
|
yuuji@0
|
98 if (!myNewsrc) { /* set news file name if not defined */
|
yuuji@0
|
99 char tmp[MAILTMPLEN];
|
yuuji@0
|
100 sprintf (tmp,"%s\\NEWSRC",myhomedir ());
|
yuuji@0
|
101 myNewsrc = cpystr (tmp);
|
yuuji@0
|
102 }
|
yuuji@0
|
103 ret = (void *) myNewsrc;
|
yuuji@0
|
104 break;
|
yuuji@0
|
105 case SET_SYSINBOX:
|
yuuji@0
|
106 if (sysInbox) fs_give ((void **) &sysInbox);
|
yuuji@0
|
107 sysInbox = cpystr ((char *) value);
|
yuuji@0
|
108 case GET_SYSINBOX:
|
yuuji@0
|
109 ret = (void *) sysInbox;
|
yuuji@0
|
110 break;
|
yuuji@0
|
111 case SET_LISTMAXLEVEL:
|
yuuji@0
|
112 list_max_level = (long) value;
|
yuuji@0
|
113 case GET_LISTMAXLEVEL:
|
yuuji@0
|
114 ret = (void *) list_max_level;
|
yuuji@0
|
115 break;
|
yuuji@0
|
116 case SET_DISABLE822TZTEXT:
|
yuuji@0
|
117 no822tztext = value ? T : NIL;
|
yuuji@0
|
118 case GET_DISABLE822TZTEXT:
|
yuuji@0
|
119 ret = (void *) (no822tztext ? VOIDT : NIL);
|
yuuji@0
|
120 break;
|
yuuji@0
|
121 case SET_BLOCKENVINIT:
|
yuuji@0
|
122 block_env_init = value ? T : NIL;
|
yuuji@0
|
123 case GET_BLOCKENVINIT:
|
yuuji@0
|
124 ret = (void *) (block_env_init ? VOIDT : NIL);
|
yuuji@0
|
125 break;
|
yuuji@0
|
126 case SET_BLOCKNOTIFY:
|
yuuji@0
|
127 mailblocknotify = (blocknotify_t) value;
|
yuuji@0
|
128 case GET_BLOCKNOTIFY:
|
yuuji@0
|
129 ret = (void *) mailblocknotify;
|
yuuji@0
|
130 break;
|
yuuji@0
|
131 }
|
yuuji@0
|
132 return ret;
|
yuuji@0
|
133 }
|
yuuji@0
|
134
|
yuuji@0
|
135 /* Write current time
|
yuuji@0
|
136 * Accepts: destination string
|
yuuji@0
|
137 * optional format of day-of-week prefix
|
yuuji@0
|
138 * format of date and time
|
yuuji@0
|
139 * flag whether to append symbolic timezone
|
yuuji@0
|
140 */
|
yuuji@0
|
141
|
yuuji@0
|
142 static void do_date (char *date,char *prefix,char *fmt,int suffix)
|
yuuji@0
|
143 {
|
yuuji@0
|
144 time_t tn = time (0);
|
yuuji@0
|
145 struct tm *t = gmtime (&tn);
|
yuuji@0
|
146 int zone = t->tm_hour * 60 + t->tm_min;
|
yuuji@0
|
147 int julian = t->tm_yday;
|
yuuji@0
|
148 t = localtime (&tn); /* get local time now */
|
yuuji@0
|
149 /* minus UTC minutes since midnight */
|
yuuji@0
|
150 zone = t->tm_hour * 60 + t->tm_min - zone;
|
yuuji@0
|
151 /* julian can be one of:
|
yuuji@0
|
152 * 36x local time is December 31, UTC is January 1, offset -24 hours
|
yuuji@0
|
153 * 1 local time is 1 day ahead of UTC, offset +24 hours
|
yuuji@0
|
154 * 0 local time is same day as UTC, no offset
|
yuuji@0
|
155 * -1 local time is 1 day behind UTC, offset -24 hours
|
yuuji@0
|
156 * -36x local time is January 1, UTC is December 31, offset +24 hours
|
yuuji@0
|
157 */
|
yuuji@0
|
158 if (julian = t->tm_yday -julian)
|
yuuji@0
|
159 zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
|
yuuji@0
|
160 if (prefix) { /* want day of week? */
|
yuuji@0
|
161 sprintf (date,prefix,days[t->tm_wday]);
|
yuuji@0
|
162 date += strlen (date); /* make next sprintf append */
|
yuuji@0
|
163 }
|
yuuji@0
|
164 /* output the date */
|
yuuji@0
|
165 sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
|
yuuji@0
|
166 t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
|
yuuji@0
|
167 if (suffix) { /* append timezone suffix if desired */
|
yuuji@0
|
168 char *tz;
|
yuuji@0
|
169 tzset (); /* get timezone from TZ environment stuff */
|
yuuji@0
|
170 tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
|
yuuji@0
|
171 if (tz && tz[0]) {
|
yuuji@0
|
172 char *s;
|
yuuji@0
|
173 for (s = tz; *s; s++) if (*s & 0x80) return;
|
yuuji@0
|
174 sprintf (date + strlen (date)," (%.50s)",tz);
|
yuuji@0
|
175 }
|
yuuji@0
|
176 }
|
yuuji@0
|
177 }
|
yuuji@0
|
178
|
yuuji@0
|
179
|
yuuji@0
|
180 /* Write current time in RFC 822 format
|
yuuji@0
|
181 * Accepts: destination string
|
yuuji@0
|
182 */
|
yuuji@0
|
183
|
yuuji@0
|
184 void rfc822_date (char *date)
|
yuuji@0
|
185 {
|
yuuji@0
|
186 do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
|
yuuji@0
|
187 no822tztext ? NIL : T);
|
yuuji@0
|
188 }
|
yuuji@0
|
189
|
yuuji@0
|
190
|
yuuji@0
|
191 /* Write current time in fixed-width RFC 822 format
|
yuuji@0
|
192 * Accepts: destination string
|
yuuji@0
|
193 */
|
yuuji@0
|
194
|
yuuji@0
|
195 void rfc822_fixed_date (char *date)
|
yuuji@0
|
196 {
|
yuuji@0
|
197 do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
|
yuuji@0
|
198 }
|
yuuji@0
|
199
|
yuuji@0
|
200
|
yuuji@0
|
201 /* Write current time in internal format
|
yuuji@0
|
202 * Accepts: destination string
|
yuuji@0
|
203 */
|
yuuji@0
|
204
|
yuuji@0
|
205 void internal_date (char *date)
|
yuuji@0
|
206 {
|
yuuji@0
|
207 do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
|
yuuji@0
|
208 }
|
yuuji@0
|
209
|
yuuji@0
|
210 /* Return random number
|
yuuji@0
|
211 */
|
yuuji@0
|
212
|
yuuji@0
|
213 long random (void)
|
yuuji@0
|
214 {
|
yuuji@0
|
215 if (!rndm) srand (rndm = (unsigned) time (0L));
|
yuuji@0
|
216 return (long) rand ();
|
yuuji@0
|
217 }
|
yuuji@0
|
218
|
yuuji@0
|
219
|
yuuji@0
|
220 /* Set alarm timer
|
yuuji@0
|
221 * Accepts: new value
|
yuuji@0
|
222 * Returns: old alarm value
|
yuuji@0
|
223 */
|
yuuji@0
|
224
|
yuuji@0
|
225 long alarm (long seconds)
|
yuuji@0
|
226 {
|
yuuji@0
|
227 long ret = alarm_countdown;
|
yuuji@0
|
228 alarm_countdown = seconds;
|
yuuji@0
|
229 return ret;
|
yuuji@0
|
230 }
|
yuuji@0
|
231
|
yuuji@0
|
232
|
yuuji@0
|
233 /* The clock ticked
|
yuuji@0
|
234 */
|
yuuji@0
|
235
|
yuuji@0
|
236 void CALLBACK clock_ticked (UINT IDEvent,UINT uReserved,DWORD dwUser,
|
yuuji@0
|
237 DWORD dwReserved1,DWORD dwReserved2)
|
yuuji@0
|
238 {
|
yuuji@0
|
239 if (alarm_rang && !--alarm_countdown) (*alarm_rang) ();
|
yuuji@0
|
240 }
|
yuuji@0
|
241
|
yuuji@0
|
242 /* Initialize server
|
yuuji@0
|
243 * Accepts: server name for syslog or NIL
|
yuuji@0
|
244 * /etc/services service name or NIL
|
yuuji@0
|
245 * alternate /etc/services service name or NIL
|
yuuji@0
|
246 * clock interrupt handler
|
yuuji@0
|
247 * kiss-of-death interrupt handler
|
yuuji@0
|
248 * hangup interrupt handler
|
yuuji@0
|
249 * termination interrupt handler
|
yuuji@0
|
250 */
|
yuuji@0
|
251
|
yuuji@0
|
252 void server_init (char *server,char *service,char *sslservice,
|
yuuji@0
|
253 void *clkint,void *kodint,void *hupint,void *trmint,
|
yuuji@0
|
254 void *staint)
|
yuuji@0
|
255 {
|
yuuji@0
|
256 if (!check_nt ()) {
|
yuuji@0
|
257 if (!auth_md5.server) fatal ("Can't run on Windows without MD5 database");
|
yuuji@0
|
258 server_nli = T; /* Windows server not logged in */
|
yuuji@0
|
259 }
|
yuuji@0
|
260 /* only do this if for init call */
|
yuuji@0
|
261 if (server && service && sslservice) {
|
yuuji@0
|
262 long port;
|
yuuji@0
|
263 struct servent *sv;
|
yuuji@0
|
264 /* set server name in syslog */
|
yuuji@0
|
265 openlog (server,LOG_PID,LOG_MAIL);
|
yuuji@0
|
266 fclose (stderr); /* possibly save a process ID */
|
yuuji@0
|
267 /* Use SSL if SSL service, or if server starts with "s" and not service */
|
yuuji@0
|
268 if (((port = tcp_serverport ()) >= 0)) {
|
yuuji@0
|
269 if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
|
yuuji@0
|
270 syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
|
yuuji@0
|
271 else if ((sv = getservbyname (sslservice,"tcp")) &&
|
yuuji@0
|
272 (port == ntohs (sv->s_port))) {
|
yuuji@0
|
273 syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
|
yuuji@0
|
274 tcp_clientaddr ());
|
yuuji@0
|
275 ssl_server_init (server);
|
yuuji@0
|
276 }
|
yuuji@0
|
277 else { /* not service or SSL service port */
|
yuuji@0
|
278 syslog (LOG_DEBUG,"port %ld service init from %s",port,
|
yuuji@0
|
279 tcp_clientaddr ());
|
yuuji@0
|
280 if (*server == 's') ssl_server_init (server);
|
yuuji@0
|
281 }
|
yuuji@0
|
282 }
|
yuuji@0
|
283 /* make sure stdout does binary */
|
yuuji@0
|
284 setmode (fileno (stdin),O_BINARY);
|
yuuji@0
|
285 setmode (fileno (stdout),O_BINARY);
|
yuuji@0
|
286 }
|
yuuji@0
|
287 alarm_rang = clkint; /* note the clock interrupt */
|
yuuji@0
|
288 timeBeginPeriod (1000); /* set the timer interval */
|
yuuji@0
|
289 timeSetEvent (1000,1000,clock_ticked,NIL,TIME_PERIODIC);
|
yuuji@0
|
290 }
|
yuuji@0
|
291
|
yuuji@0
|
292
|
yuuji@0
|
293 /* Wait for stdin input
|
yuuji@0
|
294 * Accepts: timeout in seconds
|
yuuji@0
|
295 * Returns: T if have input on stdin, else NIL
|
yuuji@0
|
296 */
|
yuuji@0
|
297
|
yuuji@0
|
298 long server_input_wait (long seconds)
|
yuuji@0
|
299 {
|
yuuji@0
|
300 fd_set rfd,efd;
|
yuuji@0
|
301 struct timeval tmo;
|
yuuji@0
|
302 FD_ZERO (&rfd);
|
yuuji@0
|
303 FD_ZERO (&efd);
|
yuuji@0
|
304 FD_SET (0,&rfd);
|
yuuji@0
|
305 FD_SET (0,&efd);
|
yuuji@0
|
306 tmo.tv_sec = seconds; tmo.tv_usec = 0;
|
yuuji@0
|
307 return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
|
yuuji@0
|
308 }
|
yuuji@0
|
309
|
yuuji@0
|
310 /* Server log in
|
yuuji@0
|
311 * Accepts: user name string
|
yuuji@0
|
312 * password string
|
yuuji@0
|
313 * authenticating user name string
|
yuuji@0
|
314 * argument count
|
yuuji@0
|
315 * argument vector
|
yuuji@0
|
316 * Returns: T if password validated, NIL otherwise
|
yuuji@0
|
317 */
|
yuuji@0
|
318
|
yuuji@0
|
319 static int gotprivs = NIL; /* once-only flag to grab privileges */
|
yuuji@0
|
320
|
yuuji@0
|
321 long server_login (char *user,char *pass,char *authuser,int argc,char *argv[])
|
yuuji@0
|
322 {
|
yuuji@0
|
323 HANDLE hdl;
|
yuuji@0
|
324 LUID tcbpriv;
|
yuuji@0
|
325 TOKEN_PRIVILEGES tkp;
|
yuuji@0
|
326 char *s;
|
yuuji@0
|
327 /* need to get privileges? */
|
yuuji@0
|
328 if (!gotprivs++ && check_nt ()) {
|
yuuji@0
|
329 /* hack for inetlisn */
|
yuuji@0
|
330 if (argc >= 2) myClientHost = argv[1];
|
yuuji@0
|
331 /* get process token and TCB priv value */
|
yuuji@0
|
332 if (!(OpenProcessToken (GetCurrentProcess (),
|
yuuji@0
|
333 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hdl) &&
|
yuuji@0
|
334 LookupPrivilegeValue ((LPSTR) NIL,SE_TCB_NAME,&tcbpriv)))
|
yuuji@0
|
335 return NIL;
|
yuuji@0
|
336 tkp.PrivilegeCount = 1; /* want to enable this privilege */
|
yuuji@0
|
337 tkp.Privileges[0].Luid = tcbpriv;
|
yuuji@0
|
338 tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
yuuji@0
|
339 /* enable it */
|
yuuji@0
|
340 AdjustTokenPrivileges (hdl,NIL,&tkp,sizeof (TOKEN_PRIVILEGES),
|
yuuji@0
|
341 (PTOKEN_PRIVILEGES) NIL,(PDWORD) NIL);
|
yuuji@0
|
342 /* make sure it won */
|
yuuji@0
|
343 if (GetLastError() != ERROR_SUCCESS) return NIL;
|
yuuji@0
|
344 }
|
yuuji@0
|
345
|
yuuji@0
|
346 /* cretins still haven't given up */
|
yuuji@0
|
347 if ((strlen (user) >= MAILTMPLEN) ||
|
yuuji@0
|
348 (authuser && (strlen (authuser) >= MAILTMPLEN)))
|
yuuji@0
|
349 syslog (LOG_ALERT,"SYSTEM BREAK-IN ATTEMPT, host=%.80s",tcp_clienthost ());
|
yuuji@0
|
350 else if (logtry > 0) { /* still have available logins? */
|
yuuji@0
|
351 /* authentication user not supported */
|
yuuji@0
|
352 if (authuser && *authuser && compare_cstring (authuser,user))
|
yuuji@0
|
353 mm_log ("Authentication id must match authorization id",ERROR);
|
yuuji@0
|
354 if (check_nt ()) { /* NT: authserver_login() call not supported */
|
yuuji@0
|
355 if (!pass) mm_log ("Unsupported authentication mechanism",ERROR);
|
yuuji@0
|
356 else if (( /* try to login and impersonate the guy */
|
yuuji@0
|
357 #ifdef LOGIN32_LOGON_NETWORK
|
yuuji@0
|
358 LogonUser (user,".",pass,LOGON32_LOGON_NETWORK,
|
yuuji@0
|
359 LOGON32_PROVIDER_DEFAULT,&hdl) ||
|
yuuji@0
|
360 #endif
|
yuuji@0
|
361 LogonUser (user,".",pass,LOGON32_LOGON_INTERACTIVE,
|
yuuji@0
|
362 LOGON32_PROVIDER_DEFAULT,&hdl) ||
|
yuuji@0
|
363 LogonUser (user,".",pass,LOGON32_LOGON_BATCH,
|
yuuji@0
|
364 LOGON32_PROVIDER_DEFAULT,&hdl) ||
|
yuuji@0
|
365 LogonUser (user,".",pass,LOGON32_LOGON_SERVICE,
|
yuuji@0
|
366 LOGON32_PROVIDER_DEFAULT,&hdl)) &&
|
yuuji@0
|
367 ImpersonateLoggedOnUser (hdl)) return env_init (user,NIL);
|
yuuji@0
|
368 }
|
yuuji@0
|
369 else { /* Win9x: done if from authserver_login() */
|
yuuji@0
|
370 if (!pass) server_nli = NIL;
|
yuuji@0
|
371 /* otherwise check MD5 database */
|
yuuji@0
|
372 else if (s = auth_md5_pwd (user)) {
|
yuuji@0
|
373 /* change NLI state based on pwd match */
|
yuuji@0
|
374 server_nli = strcmp (s,pass);
|
yuuji@0
|
375 memset (s,0,strlen (s));/* erase sensitive information */
|
yuuji@0
|
376 fs_give ((void **) &s); /* flush erased password */
|
yuuji@0
|
377 }
|
yuuji@0
|
378 /* success if no longer NLI */
|
yuuji@0
|
379 if (!server_nli) return env_init (user,NIL);
|
yuuji@0
|
380 }
|
yuuji@0
|
381 }
|
yuuji@0
|
382 s = (logtry-- > 0) ? "Login failure" : "Excessive login attempts";
|
yuuji@0
|
383 /* note the failure in the syslog */
|
yuuji@0
|
384 syslog (LOG_INFO,"%s user=%.80s host=%.80s",s,user,tcp_clienthost ());
|
yuuji@0
|
385 sleep (3); /* slow down possible cracker */
|
yuuji@0
|
386 return NIL;
|
yuuji@0
|
387 }
|
yuuji@0
|
388
|
yuuji@0
|
389 /* Authenticated server log in
|
yuuji@0
|
390 * Accepts: user name string
|
yuuji@0
|
391 * authentication user name string
|
yuuji@0
|
392 * argument count
|
yuuji@0
|
393 * argument vector
|
yuuji@0
|
394 * Returns: T if password validated, NIL otherwise
|
yuuji@0
|
395 */
|
yuuji@0
|
396
|
yuuji@0
|
397 long authserver_login (char *user,char *authuser,int argc,char *argv[])
|
yuuji@0
|
398 {
|
yuuji@0
|
399 return server_login (user,NIL,authuser,argc,argv);
|
yuuji@0
|
400 }
|
yuuji@0
|
401
|
yuuji@0
|
402
|
yuuji@0
|
403 /* Log in as anonymous daemon
|
yuuji@0
|
404 * Accepts: argument count
|
yuuji@0
|
405 * argument vector
|
yuuji@0
|
406 * Returns: T if successful, NIL if error
|
yuuji@0
|
407 */
|
yuuji@0
|
408
|
yuuji@0
|
409 long anonymous_login (int argc,char *argv[])
|
yuuji@0
|
410 {
|
yuuji@0
|
411 return server_login ("Guest",NIL,NIL,argc,argv);
|
yuuji@0
|
412 }
|
yuuji@0
|
413
|
yuuji@0
|
414
|
yuuji@0
|
415 /* Initialize environment
|
yuuji@0
|
416 * Accepts: user name
|
yuuji@0
|
417 * home directory, or NIL to use default
|
yuuji@0
|
418 * Returns: T, always
|
yuuji@0
|
419 */
|
yuuji@0
|
420
|
yuuji@0
|
421 long env_init (char *user,char *home)
|
yuuji@0
|
422 {
|
yuuji@0
|
423 /* don't init if blocked */
|
yuuji@0
|
424 if (block_env_init) return LONGT;
|
yuuji@0
|
425 if (myUserName) fatal ("env_init called twice!");
|
yuuji@0
|
426 myUserName = cpystr (user); /* remember user name */
|
yuuji@0
|
427 if (!myHomeDir) /* only if home directory not set up yet */
|
yuuji@0
|
428 myHomeDir = (home && *home) ? cpystr (home) : win_homedir (user);
|
yuuji@0
|
429 return T;
|
yuuji@0
|
430 }
|
yuuji@0
|
431
|
yuuji@0
|
432 /* Check if NT
|
yuuji@0
|
433 * Returns: T if NT, NIL if Win9x
|
yuuji@0
|
434 */
|
yuuji@0
|
435
|
yuuji@0
|
436 int check_nt (void)
|
yuuji@0
|
437 {
|
yuuji@0
|
438 if (is_nt < 0) { /* not yet set up? */
|
yuuji@0
|
439 OSVERSIONINFO ver;
|
yuuji@0
|
440 ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
yuuji@0
|
441 GetVersionEx (&ver);
|
yuuji@0
|
442 is_nt = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) ? T : NIL;
|
yuuji@0
|
443 }
|
yuuji@0
|
444 return is_nt;
|
yuuji@0
|
445 }
|
yuuji@0
|
446
|
yuuji@0
|
447
|
yuuji@0
|
448 /* Return Windows home directory
|
yuuji@0
|
449 * Accepts: user name
|
yuuji@0
|
450 * Returns: home directory
|
yuuji@0
|
451 */
|
yuuji@0
|
452
|
yuuji@0
|
453 char *win_homedir (char *user)
|
yuuji@0
|
454 {
|
yuuji@0
|
455 char *s,*t,tmp[MAILTMPLEN];
|
yuuji@0
|
456 PUSER_INFO_1 ui;
|
yuuji@0
|
457 /* Win9x default */
|
yuuji@0
|
458 if (!check_nt ()) sprintf (tmp,"%s\\My Documents",defaultDrive ());
|
yuuji@0
|
459 /* get from user info on NT */
|
yuuji@0
|
460 else if ((netapi || (netapi = LoadLibrary ("netapi32.dll"))) &&
|
yuuji@0
|
461 (getinfo ||
|
yuuji@0
|
462 (getinfo = (GETINFO) GetProcAddress (netapi,"NetUserGetInfo"))) &&
|
yuuji@0
|
463 MultiByteToWideChar (CP_ACP,0,user,strlen (user) + 1,
|
yuuji@0
|
464 (WCHAR *) tmp,MAILTMPLEN) &&
|
yuuji@0
|
465 !(*getinfo) (NIL,(LPWSTR) &tmp,1,(LPBYTE *) &ui) &&
|
yuuji@0
|
466 WideCharToMultiByte (CP_ACP,0,ui->usri1_home_dir,-1,
|
yuuji@0
|
467 tmp,MAILTMPLEN,NIL,NIL) && tmp[0]) {
|
yuuji@0
|
468 /* make sure doesn't end with delimiter */
|
yuuji@0
|
469 if ((*(s = tmp + strlen (tmp) - 1) == '\\') || (*s == '/')) *s = '\0';
|
yuuji@0
|
470 }
|
yuuji@0
|
471 /* no home dir, found Win2K user profile? */
|
yuuji@0
|
472 else if ((s = getenv ("USERPROFILE")) && (t = strrchr (s,'\\'))) {
|
yuuji@0
|
473 strncpy (tmp,s,t-s); /* copy up to user name */
|
yuuji@0
|
474 sprintf (tmp+(t-s),"\\%.100s\\My Documents",user);
|
yuuji@0
|
475 }
|
yuuji@0
|
476 /* last resort NT default */
|
yuuji@0
|
477 else sprintf (tmp,"%s\\users\\default",defaultDrive ());
|
yuuji@0
|
478 return cpystr (tmp);
|
yuuji@0
|
479 }
|
yuuji@0
|
480
|
yuuji@0
|
481
|
yuuji@0
|
482 /* Return default drive
|
yuuji@0
|
483 * Returns: default drive
|
yuuji@0
|
484 */
|
yuuji@0
|
485
|
yuuji@0
|
486 static char *defaultDrive (void)
|
yuuji@0
|
487 {
|
yuuji@0
|
488 char *s = getenv ("SystemDrive");
|
yuuji@0
|
489 return (s && *s) ? s : "C:";
|
yuuji@0
|
490 }
|
yuuji@0
|
491
|
yuuji@0
|
492 /* Return my user name
|
yuuji@0
|
493 * Accepts: pointer to optional flags
|
yuuji@0
|
494 * Returns: my user name
|
yuuji@0
|
495 */
|
yuuji@0
|
496
|
yuuji@0
|
497 char *myusername_full (unsigned long *flags)
|
yuuji@0
|
498 {
|
yuuji@0
|
499 UCHAR usr[MAILTMPLEN];
|
yuuji@0
|
500 DWORD len = MAILTMPLEN;
|
yuuji@0
|
501 char *user,*path,*d,*p,pth[MAILTMPLEN];
|
yuuji@0
|
502 char *ret = "SYSTEM";
|
yuuji@0
|
503 /* get user name if don't have it yet */
|
yuuji@0
|
504 if (!myUserName && !server_nli &&
|
yuuji@0
|
505 /* use callback, else logon name */
|
yuuji@0
|
506 ((mailusername && (user = (char *) (*mailusername) ())) ||
|
yuuji@0
|
507 (GetUserName (usr,&len) && _stricmp (user = (char *) usr,"SYSTEM")))) {
|
yuuji@0
|
508 if (block_env_init) { /* don't env_init if blocked */
|
yuuji@0
|
509 if (flags) *flags = MU_LOGGEDIN;
|
yuuji@0
|
510 return user;
|
yuuji@0
|
511 }
|
yuuji@0
|
512 /* try HOMEPATH, then HOME */
|
yuuji@0
|
513 if (p = getenv ("HOMEPATH"))
|
yuuji@0
|
514 sprintf (path = pth,"%s%s",
|
yuuji@0
|
515 (d = getenv ("HOMEDRIVE")) ? d : defaultDrive (),p);
|
yuuji@0
|
516 else if (!(path = getenv ("HOME")))
|
yuuji@0
|
517 sprintf (path = pth,"%s\\My Documents",defaultDrive ());
|
yuuji@0
|
518 /* make sure doesn't end with delimiter */
|
yuuji@0
|
519 if ((*(p = path + strlen (path) -1) == '\\') || (*p == '/')) *p = '\0';
|
yuuji@0
|
520 env_init (user,path); /* initialize environment */
|
yuuji@0
|
521 }
|
yuuji@0
|
522 if (myUserName) { /* logged in? */
|
yuuji@0
|
523 if (flags) /* Guest is an anonymous user */
|
yuuji@0
|
524 *flags = _stricmp (myUserName,"Guest") ? MU_LOGGEDIN : MU_ANONYMOUS;
|
yuuji@0
|
525 ret = myUserName; /* return user name */
|
yuuji@0
|
526 }
|
yuuji@0
|
527 else if (flags) *flags = MU_NOTLOGGEDIN;
|
yuuji@0
|
528 return ret;
|
yuuji@0
|
529 }
|
yuuji@0
|
530
|
yuuji@0
|
531 /* Return my local host name
|
yuuji@0
|
532 * Returns: my local host name
|
yuuji@0
|
533 */
|
yuuji@0
|
534
|
yuuji@0
|
535 char *mylocalhost (void)
|
yuuji@0
|
536 {
|
yuuji@0
|
537 if (!myLocalHost) {
|
yuuji@0
|
538 char tmp[MAILTMPLEN];
|
yuuji@0
|
539 if (!wsa_initted++) { /* init Windows Sockets */
|
yuuji@0
|
540 WSADATA wsock;
|
yuuji@0
|
541 if (WSAStartup (WINSOCK_VERSION,&wsock)) {
|
yuuji@0
|
542 wsa_initted = 0;
|
yuuji@0
|
543 return "random-pc"; /* try again later? */
|
yuuji@0
|
544 }
|
yuuji@0
|
545 }
|
yuuji@0
|
546 myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
|
yuuji@0
|
547 "random-pc" : tcp_canonical (tmp));
|
yuuji@0
|
548 }
|
yuuji@0
|
549 return myLocalHost;
|
yuuji@0
|
550 }
|
yuuji@0
|
551
|
yuuji@0
|
552 /* Return my home directory name
|
yuuji@0
|
553 * Returns: my home directory name
|
yuuji@0
|
554 */
|
yuuji@0
|
555
|
yuuji@0
|
556 char *myhomedir ()
|
yuuji@0
|
557 {
|
yuuji@0
|
558 if (!myHomeDir) myusername ();/* initialize if first time */
|
yuuji@0
|
559 return myHomeDir ? myHomeDir : "";
|
yuuji@0
|
560 }
|
yuuji@0
|
561
|
yuuji@0
|
562
|
yuuji@0
|
563 /* Return system standard INBOX
|
yuuji@0
|
564 * Accepts: buffer string
|
yuuji@0
|
565 */
|
yuuji@0
|
566
|
yuuji@0
|
567 char *sysinbox ()
|
yuuji@0
|
568 {
|
yuuji@0
|
569 char tmp[MAILTMPLEN];
|
yuuji@0
|
570 if (!sysInbox) { /* initialize if first time */
|
yuuji@0
|
571 if (check_nt ()) sprintf (tmp,MAILFILE,myUserName);
|
yuuji@0
|
572 else sprintf (tmp,"%s\\INBOX",myhomedir ());
|
yuuji@0
|
573 sysInbox = cpystr (tmp); /* system inbox is from mail spool */
|
yuuji@0
|
574 }
|
yuuji@0
|
575 return sysInbox;
|
yuuji@0
|
576 }
|
yuuji@0
|
577
|
yuuji@0
|
578
|
yuuji@0
|
579 /* Return mailbox directory name
|
yuuji@0
|
580 * Accepts: destination buffer
|
yuuji@0
|
581 * directory prefix
|
yuuji@0
|
582 * name in directory
|
yuuji@0
|
583 * Returns: file name or NIL if error
|
yuuji@0
|
584 */
|
yuuji@0
|
585
|
yuuji@0
|
586 char *mailboxdir (char *dst,char *dir,char *name)
|
yuuji@0
|
587 {
|
yuuji@0
|
588 char tmp[MAILTMPLEN];
|
yuuji@0
|
589 if (dir || name) { /* if either argument provided */
|
yuuji@0
|
590 if (dir) {
|
yuuji@0
|
591 if (strlen (dir) > NETMAXMBX) return NIL;
|
yuuji@0
|
592 strcpy (tmp,dir); /* write directory prefix */
|
yuuji@0
|
593 }
|
yuuji@0
|
594 else tmp[0] = '\0'; /* otherwise null string */
|
yuuji@0
|
595 if (name) {
|
yuuji@0
|
596 if (strlen (name) > NETMAXMBX) return NIL;
|
yuuji@0
|
597 strcat (tmp,name); /* write name in directory */
|
yuuji@0
|
598 }
|
yuuji@0
|
599 /* validate name, return its name */
|
yuuji@0
|
600 if (!mailboxfile (dst,tmp)) return NIL;
|
yuuji@0
|
601 }
|
yuuji@0
|
602 else strcpy (dst,myhomedir());/* no arguments, wants home directory */
|
yuuji@0
|
603 return dst; /* return the name */
|
yuuji@0
|
604 }
|
yuuji@0
|
605
|
yuuji@0
|
606 /* Return mailbox file name
|
yuuji@0
|
607 * Accepts: destination buffer
|
yuuji@0
|
608 * mailbox name
|
yuuji@0
|
609 * Returns: file name or empty string for driver-selected INBOX or NIL if error
|
yuuji@0
|
610 */
|
yuuji@0
|
611
|
yuuji@0
|
612 char *mailboxfile (char *dst,char *name)
|
yuuji@0
|
613 {
|
yuuji@0
|
614 char homedev[3];
|
yuuji@0
|
615 char *dir = myhomedir ();
|
yuuji@0
|
616 if (dir[0] && isalpha (dir[0]) && (dir[1] == ':')) {
|
yuuji@0
|
617 homedev[0] = dir[0]; /* copy home device */
|
yuuji@0
|
618 homedev[1] = dir[1];
|
yuuji@0
|
619 homedev[2] = '\0';
|
yuuji@0
|
620 }
|
yuuji@0
|
621 else homedev[0] = '\0'; /* ??no home device?? */
|
yuuji@0
|
622 *dst = '\0'; /* default to empty string */
|
yuuji@0
|
623 /* check for INBOX */
|
yuuji@0
|
624 if (!compare_cstring (name,"INBOX"));
|
yuuji@0
|
625 /* reject names with / */
|
yuuji@0
|
626 else if (strchr (name,'/')) dst = NIL;
|
yuuji@0
|
627 else switch (*name) {
|
yuuji@0
|
628 case '#': /* namespace names */
|
yuuji@0
|
629 if (((name[1] == 'u') || (name[1] == 'U')) &&
|
yuuji@0
|
630 ((name[2] == 's') || (name[2] == 'S')) &&
|
yuuji@0
|
631 ((name[3] == 'e') || (name[3] == 'E')) &&
|
yuuji@0
|
632 ((name[4] == 'r') || (name[4] == 'R')) && (name[5] == '.')) {
|
yuuji@0
|
633 /* copy user name to destination buffer */
|
yuuji@0
|
634 for (dir = dst,name += 6; *name && (*name != '\\'); *dir++ = *name++);
|
yuuji@0
|
635 *dir++ = '\0'; /* tie off user name */
|
yuuji@0
|
636 /* look up homedir for user name */
|
yuuji@0
|
637 if (dir = win_homedir (dst)) {
|
yuuji@0
|
638 /* build resulting name */
|
yuuji@0
|
639 sprintf (dst,"%s\\%s",dir,name);
|
yuuji@0
|
640 fs_give ((void **) &dir);
|
yuuji@0
|
641 }
|
yuuji@0
|
642 else dst = NIL;
|
yuuji@0
|
643 }
|
yuuji@0
|
644 else dst = NIL; /* unknown namespace name */
|
yuuji@0
|
645 break;
|
yuuji@0
|
646 case '\\': /* absolute path on default drive? */
|
yuuji@0
|
647 sprintf (dst,"%s%s",homedev,name);
|
yuuji@0
|
648 break;
|
yuuji@0
|
649 default: /* any other name */
|
yuuji@0
|
650 if (name[1] == ':') { /* some other drive? */
|
yuuji@0
|
651 if (name[2] == '\\') strcpy (dst,name);
|
yuuji@0
|
652 else sprintf (dst,"%c:\\%s",name[0],name+2);
|
yuuji@0
|
653 }
|
yuuji@0
|
654 /* build home-directory relative name */
|
yuuji@0
|
655 else sprintf (dst,"%s\\%s",dir,name);
|
yuuji@0
|
656 }
|
yuuji@0
|
657 return dst; /* return it */
|
yuuji@0
|
658 }
|
yuuji@0
|
659
|
yuuji@0
|
660 /* Lock file name
|
yuuji@0
|
661 * Accepts: return buffer for file name
|
yuuji@0
|
662 * file name
|
yuuji@0
|
663 * locking to be placed on file if non-NIL
|
yuuji@0
|
664 * Returns: file descriptor of lock or -1 if error
|
yuuji@0
|
665 */
|
yuuji@0
|
666
|
yuuji@0
|
667 int lockname (char *lock,char *fname,int op)
|
yuuji@0
|
668 {
|
yuuji@0
|
669 int ld;
|
yuuji@0
|
670 char c,*s;
|
yuuji@0
|
671 /* Win2K and Win98 have TEMP under windir */
|
yuuji@0
|
672 if (!((s = lockdir (lock,getenv ("windir"),"TEMP")) ||
|
yuuji@0
|
673 /* NT4, NT3.x and Win95 use one of these */
|
yuuji@0
|
674 (s = lockdir (lock,getenv ("TEMP"),NIL)) ||
|
yuuji@0
|
675 (s = lockdir (lock,getenv ("TMP"),NIL)) ||
|
yuuji@0
|
676 (s = lockdir (lock,getenv ("TMPDIR"),NIL)) ||
|
yuuji@0
|
677 /* try one of these */
|
yuuji@0
|
678 (s = lockdir (lock,defaultDrive (),"WINNT\\TEMP")) ||
|
yuuji@0
|
679 (s = lockdir (lock,defaultDrive (),"WINDOWS\\TEMP")) ||
|
yuuji@0
|
680 /* C:\TEMP is last resort */
|
yuuji@0
|
681 (s = lockdir (lock,defaultDrive (),"TEMP")))) {
|
yuuji@0
|
682 mm_log ("Unable to find temporary directory",ERROR);
|
yuuji@0
|
683 return -1;
|
yuuji@0
|
684 }
|
yuuji@0
|
685 /* generate file name */
|
yuuji@0
|
686 while (c = *fname++) switch (c) {
|
yuuji@0
|
687 case '/': case '\\': case ':':
|
yuuji@0
|
688 *s++ = '!'; /* convert bad chars to ! */
|
yuuji@0
|
689 break;
|
yuuji@0
|
690 default:
|
yuuji@0
|
691 *s++ = c;
|
yuuji@0
|
692 break;
|
yuuji@0
|
693 }
|
yuuji@0
|
694 *s++ = c; /* tie off name */
|
yuuji@0
|
695 /* get the lock */
|
yuuji@0
|
696 if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op)
|
yuuji@0
|
697 flock (ld,op); /* apply locking function */
|
yuuji@0
|
698 return ld; /* return locking file descriptor */
|
yuuji@0
|
699 }
|
yuuji@0
|
700
|
yuuji@0
|
701 /* Build lock directory, check to see if it exists
|
yuuji@0
|
702 * Accepts: return buffer for lock directory
|
yuuji@0
|
703 * first part of possible name
|
yuuji@0
|
704 * optional second part
|
yuuji@0
|
705 * Returns: pointer to end of buffer if buffer has a good name, else NIL
|
yuuji@0
|
706 */
|
yuuji@0
|
707
|
yuuji@0
|
708 char *lockdir (char *lock,char *first,char *last)
|
yuuji@0
|
709 {
|
yuuji@0
|
710 struct stat sbuf;
|
yuuji@0
|
711 char c,*s;
|
yuuji@0
|
712 if (first && *first) { /* first part must be non-NIL */
|
yuuji@0
|
713 /* copy first part */
|
yuuji@0
|
714 for (s = lock; c = *first++; *s++ = (c == '/') ? '\\' : c);
|
yuuji@0
|
715 if (last && *last) { /* copy last part if specified */
|
yuuji@0
|
716 /* write trailing \ in case not in first */
|
yuuji@0
|
717 if (s[-1] != '\\') *s++ = '\\';
|
yuuji@0
|
718 while (c = *last++) *s++ = (c == '/') ? '\\' : c;
|
yuuji@0
|
719 }
|
yuuji@0
|
720 if (s[-1] == '\\') --s; /* delete trailing \ if any */
|
yuuji@0
|
721 *s = s[1] = '\0'; /* tie off name at this point */
|
yuuji@0
|
722 if (!stat (lock,&sbuf)) { /* does the name exist? */
|
yuuji@0
|
723 *s++ = '\\'; /* yes, reinstall trailing \ */
|
yuuji@0
|
724 return s; /* return the name */
|
yuuji@0
|
725 }
|
yuuji@0
|
726 }
|
yuuji@0
|
727 return NIL; /* failed */
|
yuuji@0
|
728 }
|
yuuji@0
|
729
|
yuuji@0
|
730
|
yuuji@0
|
731 /* Unlock file descriptor
|
yuuji@0
|
732 * Accepts: file descriptor
|
yuuji@0
|
733 * lock file name from lockfd()
|
yuuji@0
|
734 */
|
yuuji@0
|
735
|
yuuji@0
|
736 void unlockfd (int fd,char *lock)
|
yuuji@0
|
737 {
|
yuuji@0
|
738 flock (fd,LOCK_UN); /* unlock it */
|
yuuji@0
|
739 close (fd); /* close it */
|
yuuji@0
|
740 }
|
yuuji@0
|
741
|
yuuji@0
|
742
|
yuuji@0
|
743 /* Determine default prototype stream to user
|
yuuji@0
|
744 * Accepts: type (NIL for create, T for append)
|
yuuji@0
|
745 * Returns: default prototype stream
|
yuuji@0
|
746 */
|
yuuji@0
|
747
|
yuuji@0
|
748 MAILSTREAM *default_proto (long type)
|
yuuji@0
|
749 {
|
yuuji@0
|
750 extern MAILSTREAM CREATEPROTO,APPENDPROTO;
|
yuuji@0
|
751 return type ? &APPENDPROTO : &CREATEPROTO;
|
yuuji@0
|
752 }
|
yuuji@0
|
753
|
yuuji@0
|
754 /* Default block notify routine
|
yuuji@0
|
755 * Accepts: reason for calling
|
yuuji@0
|
756 * data
|
yuuji@0
|
757 * Returns: data
|
yuuji@0
|
758 */
|
yuuji@0
|
759
|
yuuji@0
|
760 void *mm_blocknotify (int reason,void *data)
|
yuuji@0
|
761 {
|
yuuji@0
|
762 void *ret = data;
|
yuuji@0
|
763 switch (reason) {
|
yuuji@0
|
764 case BLOCK_SENSITIVE: /* entering sensitive code */
|
yuuji@0
|
765 ret = (void *) alarm (0);
|
yuuji@0
|
766 break;
|
yuuji@0
|
767 case BLOCK_NONSENSITIVE: /* exiting sensitive code */
|
yuuji@0
|
768 if ((unsigned int) data) alarm ((unsigned int) data);
|
yuuji@0
|
769 break;
|
yuuji@0
|
770 default: /* ignore all other reasons */
|
yuuji@0
|
771 break;
|
yuuji@0
|
772 }
|
yuuji@0
|
773 return ret;
|
yuuji@0
|
774 }
|