1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <string.h>
5
6 #include "lsg2.h"
7
8 const char hextable[] = "0123456789ABCDEF";
9
decode_pathinfo(const char * pathinfo)10 int decode_pathinfo(const char *pathinfo)
11 {
12 char *ptr;
13 char *buffer;
14
15 buffer = strdup(pathinfo);
16
17 ptr = buffer;
18
19 ptr = strtok(ptr,"/");
20
21 while (ptr ? *ptr : 0) {
22 char *ptr2;
23 char *ptr2lower;
24
25 ptr2 = strchr(ptr,'=');
26 if (ptr2) {
27 *ptr2++ = 0;
28
29 ptr2lower = LMAPI->lowerstr(ptr2);
30
31 LMAPI->log_printf(18,"Parsing pathinfo: %s (%s)\n", ptr, ptr2);
32
33 /* Take our pathinfo pieces */
34 switch (tolower(*ptr)) {
35 case 'l':
36 LMAPI->set_var("lcgi-list",ptr2lower,VAR_GLOBAL);
37 break;
38 case 'u':
39 LMAPI->set_var("lcgi-user",ptr2lower,VAR_GLOBAL);
40 break;
41 case 'c':
42 LMAPI->set_var("lcgi-cookie",ptr2,VAR_GLOBAL);
43 break;
44 case 'm':
45 LMAPI->set_var("lcgi-mode",ptr2,VAR_GLOBAL);
46 break;
47 case 'p':
48 LMAPI->set_var("lcgi-pass",ptr2,VAR_GLOBAL);
49 break;
50 }
51
52 free(ptr2lower);
53
54 *(ptr2 - 1) = '=';
55
56 }
57 ptr = strtok(NULL,"/");
58 }
59 free(buffer);
60 return 1;
61 }
62
63 /* Decode a key=value pair */
decode_param(const char * param)64 int decode_param(const char *param)
65 {
66 char *buffer;
67 char *paramname, *endvalue, *ptr, *ptr2;
68 int returnval;
69
70 /* Get our working buffer... */
71 buffer = (char *)strdup(param);
72
73 /* ...and split it into the key and encoded value */
74 ptr = (char *)strchr(buffer,'=');
75 *ptr = 0;
76 paramname = (char *)strdup(buffer);
77
78 /* Set up the workspace for the value decoding */
79 endvalue = (char *)malloc(strlen(param));
80 memset(endvalue,0,strlen(param));
81
82 ptr2 = endvalue;
83
84 ptr++;
85
86 /* Walk the encoded value, changing from CGI-encoded to
87 normal text. */
88 while (*ptr) {
89 if (*ptr == '+') {
90 *ptr2 = ' ';
91 } else if (*ptr == '%') {
92 char temp;
93 const char *temp2;
94 int value;
95
96 /* Strange, ugly, scary hack to decode hexadecimal,
97 because sscanf was doing odd things. */
98 temp = toupper(*(ptr + 1));
99 temp2 = strchr(hextable, temp);
100 value = (temp2 - &hextable[0]) * 16;
101 temp = toupper(*(ptr + 2));
102 temp2 = strchr(hextable, temp);
103 value += temp2 - &hextable[0];
104
105 ptr += 2;
106 *ptr2 = value;
107 } else *ptr2 = *ptr;
108
109 ptr2++;
110 ptr++;
111 }
112
113 *ptr2 = 0;
114
115 if (strlen(endvalue)) {
116 char *endvaluelower;
117
118 /* We can only set variables beginning in lcgi- this way,
119 for security reasons.
120
121 HOWEVER, lcgipl- is also allowed and will be stored in a
122 temporary key/value structure, for the purposes of things
123 like setting flags on yourself and doing the list
124 configuration screen. */
125
126 endvaluelower = LMAPI->lowerstr(endvalue);
127
128 if (strncasecmp(paramname,"lcgi-",5) == 0) {
129 if ((strcasecmp(paramname,"lcgi-user") == 0) ||
130 (strcasecmp(paramname,"lcgi-list") == 0)) {
131 LMAPI->set_var(paramname,endvaluelower,VAR_GLOBAL);
132 } else {
133 LMAPI->set_var(paramname,endvalue,VAR_GLOBAL);
134 }
135
136 returnval = 1;
137 } else {
138 if (strncasecmp(paramname,"lcgipl-",7) == 0) {
139 LMAPI->add_cgi_tempvar((paramname+7),endvalue);
140 }
141 returnval = 0;
142 }
143
144 free(endvaluelower);
145 } else returnval = 0;
146
147 /* It's always good to clean up after ourselves. */
148 free(paramname);
149 free(endvalue);
150 free(buffer);
151
152 return(returnval);
153 }
154
lsg2_internal_error_fallback(const char * errortext)155 void lsg2_internal_error_fallback(const char *errortext)
156 {
157 printf("<body bgcolor=#ffffff text=#000000>\n");
158 printf("<title>LSG/2 Internal Error: %s</title>\n", errortext);
159 printf("<font size=+3>LSG/2 Error: %s</font>\n", errortext);
160 printf("<HR>\n");
161 printf("An internal and unrecoverable error has occured.\n");
162 printf("Please contact <a href=\"mailto:%s\">%s</a> and report\n",
163 LMAPI->get_string("listserver-admin"),
164 LMAPI->get_string("listserver-admin"));
165 printf("how you got this error.");
166 printf("<HR>\n");
167 }
168
lsg2_internal_error(const char * errortext)169 void lsg2_internal_error(const char *errortext)
170 {
171 LMAPI->set_var("lcgi-error", errortext, VAR_TEMP);
172 if (!LMAPI->cgi_unparse_template("error"))
173 lsg2_internal_error_fallback(errortext);
174 }
175
MODE_HANDLER(mode_lsg2)176 MODE_HANDLER(mode_lsg2)
177 {
178 int len;
179 char *inbuffer, *ptr;
180 struct listserver_cgi_mode *curmode;
181 char *tbuf;
182
183 /* turn off loose host matching */
184 LMAPI->set_var("no-loose-domain-match", "yes", VAR_GLOBAL);
185
186 /* Generic header */
187
188 if (LMAPI->get_bool("lsg2-iis-support"))
189 printf("HTTP/1.1 200 OK\n");
190
191 printf("Content-type: text/html\n");
192 printf("LSG2-Version: %s\n", LSG2_VERSION);
193 printf("\n\n");
194
195 /* Let's do a sanity check.. */
196 if (!LMAPI->get_var("lsg2-cgi-url") ||
197 !LMAPI->get_var("cgi-template-dir")) {
198 lsg2_internal_error("Incorrectly Configured");
199 return MODE_END;
200 }
201
202 /* Get the content-length variable... */
203 ptr = getenv("CONTENT_LENGTH");
204
205 /* ...making sure it's valid... */
206 if (ptr ? atoi(ptr) : 0) {
207 /* ...and read in the POST data. */
208 len = atoi(ptr);
209
210 inbuffer = (char *)malloc(len + 2);
211 memset(inbuffer,0,len + 2);
212 fgets(inbuffer,len + 1,stdin);
213
214 /* Walk the set of keys */
215 ptr = (char *)strtok(inbuffer,"&");
216
217 while(ptr ? *ptr : 0) {
218 decode_param(ptr);
219 ptr = (char *)strtok(NULL,"&");
220 }
221
222 /* ...and clean up after ourselves. */
223 free(inbuffer);
224 }
225
226 /* Do we have a PATH_INFO variable? */
227 ptr = getenv("PATH_INFO");
228 if (ptr) {
229 LMAPI->set_var("tlcgi-pathinfo",ptr,VAR_GLOBAL);
230 decode_pathinfo(ptr);
231 }
232
233 /* Now, any variables in the POST'd form that began with
234 'lcgi-' and which are valid Listar variables have been
235 set, so we can simply continue along. */
236
237 if (LMAPI->get_var("lcgi-mode")) {
238 curmode = LMAPI->find_cgi_mode(LMAPI->get_string("lcgi-mode"));
239 tbuf = strdup(LMAPI->get_string("lcgi-mode"));
240 } else {
241 if (LMAPI->get_var("lcgi-user") && (LMAPI->get_var("lcgi-pass") ||
242 LMAPI->get_var("lcgi-cookie"))) {
243 if (LMAPI->get_var("lcgi-list")) {
244 curmode = LMAPI->find_cgi_mode("listmenu");
245 tbuf = strdup("listmenu");
246 } else {
247 curmode = LMAPI->find_cgi_mode("login");
248 tbuf = strdup("login");
249 }
250 } else {
251 curmode = LMAPI->find_cgi_mode("default");
252 tbuf = strdup("default");
253 }
254 }
255
256 if (LMAPI->get_var("lcgi-list")) {
257 LMAPI->set_context_list(LMAPI->get_string("lcgi-list"));
258 }
259
260 LMAPI->set_var("lcgi-mode",tbuf,VAR_GLOBAL);
261
262 LMAPI->set_var("lcgi-remote-host",getenv("REMOTE_ADDR"),VAR_GLOBAL);
263 LMAPI->set_var("lcgi-server-soft",getenv("SERVER_SOFTWARE"),VAR_GLOBAL);
264
265 if (!curmode) {
266 lsg2_internal_error("Invalid Mode");
267 printf("<!-- Mode was '%s' -->\n", tbuf);
268 free(tbuf);
269 return MODE_END;
270 } else {
271 free(tbuf);
272 (curmode->mode)();
273 return MODE_END;
274 }
275 }
276
CMDARG_HANDLER(cmdarg_lsg2)277 CMDARG_HANDLER(cmdarg_lsg2)
278 {
279 LMAPI->set_var("mode","lsg2",VAR_GLOBAL);
280 LMAPI->set_var("fakequeue","yes",VAR_GLOBAL);
281
282 return CMDARG_OK;
283 }
284
lsg2_update_cookie(char * buffer,int length)285 int lsg2_update_cookie(char *buffer, int length)
286 {
287 const char *oldcookie;
288 const char *curcookie;
289 char cookiefile[BIG_BUF];
290 char cookie[BIG_BUF];
291 char databuf[BIG_BUF];
292 time_t now;
293 int seconds;
294 const char *fromaddy = LMAPI->get_string("lcgi-user");
295 const char *remote = LMAPI->get_string("lcgi-remote-host");
296
297 if (!LMAPI->get_var("lcgi-user")) {
298 lsg2_internal_error("Invalid form data, no user.");
299 return 0;
300 }
301
302 LMAPI->buffer_printf(cookiefile, sizeof(cookiefile) - 1, "%s/SITEDATA/cookies",
303 LMAPI->get_string("lists-root"));
304
305 time(&now);
306 seconds = LMAPI->get_seconds("lsg2-cookie-duration");
307 now += seconds;
308
309 #ifdef OBSDMOD
310 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1, "%lX;%s;%s", (long)now, fromaddy, remote);
311 #else
312 #ifdef DEC_UNIX
313 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1, "%X;%s;%s", now, fromaddy, remote);
314 #else
315 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1, "%lX;%s;%s", (long)now, fromaddy, remote);
316 #endif /* DEC_UNIX */
317 #endif /* OBSDMOD */
318
319 oldcookie = LMAPI->find_cookie(cookiefile,'W',
320 LMAPI->get_string("lcgi-user"));
321 curcookie = LMAPI->get_var("lcgi-cookie");
322
323 if ((!oldcookie && !curcookie && (strcasecmp(LMAPI->get_var("lcgi-mode"),"passwd") == 0)) ||
324 (curcookie && !LMAPI->match_cookie(curcookie,LMAPI->get_string("lcgi-user")))) {
325 char forgeWarning[BIG_BUF];
326
327 lsg2_internal_error("Cookie may be forged?");
328
329 LMAPI->set_var("task-form-subject","Possible attempted forgery!",
330 VAR_TEMP);
331 LMAPI->task_heading(LMAPI->get_var("list-owner"));
332 LMAPI->buffer_printf(forgeWarning,BIG_BUF - 1,
333 "Attempted forgery from %s detected!",
334 LMAPI->get_var("lcgi-remote-host"));
335 LMAPI->smtp_body_line(forgeWarning);
336 LMAPI->smtp_body_line("");
337 LMAPI->smtp_body_line("A user on the host listed above may have tried to forge credentials to LSG/2 as");
338 LMAPI->smtp_body_line(LMAPI->get_var("lcgi-user"));
339 LMAPI->task_ending();
340 return 0;
341 }
342
343 if (curcookie || oldcookie) {
344
345 if (oldcookie) {
346 char *tmp;
347 char *cookieuser;
348 char *cookiehost;
349
350 tmp = strstr(oldcookie," : ");
351 *tmp = '\0';
352 tmp += 3;
353
354 if (curcookie ? strcmp(oldcookie,curcookie) != 0 : 0) {
355 lsg2_internal_error("Authorization failed!");
356 return 0;
357 }
358
359 cookieuser = strchr(tmp,';');
360 cookieuser++;
361 cookiehost = strchr(cookieuser,';');
362 *cookiehost++ = 0;
363
364 /* This should NEVER, EVER happen. Ever. But just to be
365 * paranoid... */
366 if (strcasecmp(cookieuser,fromaddy) != 0) {
367 lsg2_internal_error("Internal authorization routine fault.");
368 return 0;
369 }
370
371 /* Our hosts don't match, delete old cookie and generate a
372 * new one, then send it to the user. */
373 if (strcasecmp(cookiehost,remote) != 0) {
374 LMAPI->del_cookie(cookiefile,oldcookie);
375 LMAPI->set_var("cookie-for", fromaddy, VAR_TEMP);
376 if(!LMAPI->request_cookie(cookiefile, &cookie[0], 'W', databuf)) {
377 lsg2_internal_error("Unable to generate authorization code.");
378 LMAPI->filesys_error(cookiefile);
379 return 0;
380 }
381 LMAPI->buffer_printf(buffer,length - 1,"%s",cookie);
382 LMAPI->log_printf(2,"CGI: %s logged in (%s)\n",
383 LMAPI->get_var("lcgi-user"),
384 LMAPI->get_var("lcgi-remote-host"));
385 if (!LMAPI->get_var("lcgi-pass")) {
386 LMAPI->set_var("task-form-subject","Web Authorization Code",
387 VAR_TEMP);
388 LMAPI->task_heading(fromaddy);
389 LMAPI->smtp_body_line("Someone, perhaps you, has attempted to log in using your");
390 LMAPI->smtp_body_line("e-mail address at the mailing list interface at:");
391 LMAPI->smtp_body_line(LMAPI->get_string("lsg2-cgi-url"));
392 LMAPI->smtp_body_line("");
393 LMAPI->smtp_body_line("In order to continue, please paste the following line into");
394 LMAPI->smtp_body_line("the web form. (This code will expire if not used.)");
395 LMAPI->smtp_body_line("");
396 LMAPI->smtp_body_line(cookie);
397 LMAPI->smtp_body_line("");
398 LMAPI->smtp_body_line("Alternatively, you can go to:");
399 if (!LMAPI->get_var("lcgi-list"))
400 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1,
401 "%s/u=%s/m=login/c=%s", LMAPI->get_string("lsg2-cgi-url"),
402 LMAPI->get_string("lcgi-user"), cookie);
403 else
404 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1,
405 "%s/u=%s/m=listmenu/l=%s/c=%s",
406 LMAPI->get_string("lsg2-cgi-url"),
407 LMAPI->get_string("lcgi-user"), LMAPI->get_string("list"),
408 cookie);
409 LMAPI->smtp_body_line(databuf);
410 LMAPI->task_ending();
411 LMAPI->clean_var("cookie-for", VAR_TEMP);
412 return 2;
413 } else {
414 LMAPI->set_var("lcgi-cookie",cookie,VAR_GLOBAL);
415 return 1;
416 }
417 }
418
419 /* Are we logging in again from the same machine? If so,
420 * we just re-mail the cookie. */
421 if (!curcookie && oldcookie) {
422 LMAPI->log_printf(2,"CGI: %s logged in (%s)\n",
423 LMAPI->get_var("lcgi-user"),LMAPI->get_var("lcgi-remote-host"));
424 if (!LMAPI->get_var("lcgi-pass")) {
425 LMAPI->set_var("task-form-subject","Web Authorization Code (Re-mail)",
426 VAR_TEMP);
427 LMAPI->task_heading(fromaddy);
428 LMAPI->smtp_body_line("Someone, perhaps you, has attempted to log in using your");
429 LMAPI->smtp_body_line("e-mail address at the mailing list interface at:");
430 LMAPI->smtp_body_line(LMAPI->get_string("lsg2-cgi-url"));
431 LMAPI->smtp_body_line("");
432 LMAPI->smtp_body_line("As you already had an authorization code that had not yet");
433 LMAPI->smtp_body_line("expired, it is being re-mailed to you.");
434 LMAPI->smtp_body_line("");
435 LMAPI->smtp_body_line("In order to continue, please paste the following line into");
436 LMAPI->smtp_body_line("the web form. (This code will expire if not used.)");
437 LMAPI->smtp_body_line("");
438 LMAPI->smtp_body_line(oldcookie);
439 LMAPI->smtp_body_line("");
440 LMAPI->smtp_body_line("Alternatively, you can go to:");
441 if (!LMAPI->get_var("lcgi-list"))
442 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1,
443 "%s/u=%s/m=login/c=%s", LMAPI->get_string("lsg2-cgi-url"),
444 LMAPI->get_string("lcgi-user"), oldcookie);
445 else
446 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1,
447 "%s/u=%s/m=listmenu/l=%s/c=%s",
448 LMAPI->get_string("lsg2-cgi-url"),
449 LMAPI->get_string("lcgi-user"), LMAPI->get_string("lcgi-list"),
450 oldcookie);
451 LMAPI->smtp_body_line(databuf);
452 LMAPI->task_ending();
453 LMAPI->clean_var("cookie-for", VAR_TEMP);
454 free((char *)oldcookie);
455 return 2;
456 } else {
457 LMAPI->set_var("lcgi-cookie",oldcookie,VAR_GLOBAL);
458 return 1;
459 }
460 }
461
462 if (!LMAPI->modify_cookie(cookiefile, oldcookie, databuf)) {
463 lsg2_internal_error("Unable to renew authorization.");
464 LMAPI->filesys_error(cookiefile);
465 free((char *)oldcookie);
466 return 0;
467 }
468 LMAPI->buffer_printf(buffer,length - 1,"%s",oldcookie);
469 free((char *)oldcookie);
470 return 1;
471 } else {
472 lsg2_internal_error("Authorization failed: cookie expired?");
473 return 0;
474 }
475 } else {
476 LMAPI->set_var("cookie-for", fromaddy, VAR_TEMP);
477 if(!LMAPI->request_cookie(cookiefile, &cookie[0], 'W', databuf)) {
478 lsg2_internal_error("Unable to generate authorization code.");
479 LMAPI->filesys_error(cookiefile);
480 return 0;
481 }
482 LMAPI->buffer_printf(buffer,length - 1,"%s",cookie);
483 LMAPI->log_printf(2,"CGI: %s logged in (%s)\n",
484 LMAPI->get_var("lcgi-user"),LMAPI->get_var("lcgi-remote-host"));
485 if (!LMAPI->get_var("lcgi-pass")) {
486 LMAPI->set_var("task-form-subject","Web Authorization Code",
487 VAR_TEMP);
488 LMAPI->task_heading(fromaddy);
489 LMAPI->smtp_body_line("Someone, perhaps you, has attempted to log in using your");
490 LMAPI->smtp_body_line("e-mail address at the mailing list interface at:");
491 LMAPI->smtp_body_line(LMAPI->get_string("lsg2-cgi-url"));
492 LMAPI->smtp_body_line("");
493 LMAPI->smtp_body_line("In order to continue, please paste the following line into");
494 LMAPI->smtp_body_line("the web form. (This code will expire if not used.)");
495 LMAPI->smtp_body_line("");
496 LMAPI->smtp_body_line(cookie);
497 LMAPI->smtp_body_line("");
498 LMAPI->smtp_body_line("Alternatively, you can go to:");
499 if (!LMAPI->get_var("lcgi-list"))
500 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1,
501 "%s/u=%s/m=login/c=%s", LMAPI->get_string("lsg2-cgi-url"),
502 LMAPI->get_string("lcgi-user"), cookie);
503 else
504 LMAPI->buffer_printf(databuf, sizeof(databuf) - 1,
505 "%s/u=%s/m=listmenu/l=%s/c=%s",
506 LMAPI->get_string("lsg2-cgi-url"),
507 LMAPI->get_string("lcgi-user"), LMAPI->get_string("lcgi-list"),
508 cookie);
509 LMAPI->smtp_body_line(databuf);
510 LMAPI->task_ending();
511 LMAPI->clean_var("cookie-for", VAR_TEMP);
512 return 2;
513 } else {
514 LMAPI->set_var("lcgi-cookie",cookie,VAR_GLOBAL);
515 return 1;
516 }
517 }
518 }
519
lsg2_html_textfile(const char * filename)520 void lsg2_html_textfile(const char *filename)
521 {
522 char buffer[BIG_BUF];
523 FILE *infile;
524 int inchar;
525
526 LMAPI->buffer_printf(buffer, sizeof(buffer) - 1, "%s.unparsetext", LMAPI->get_string("queuefile"));
527
528 if (!LMAPI->liscript_parse_file(filename,buffer)) {
529 printf("<P><font size=+2><b>Unable to display textfile!</b></font></P>\n");
530 printf("<!-- Error on file: %s or %s -->\n", filename, buffer);
531 LMAPI->unlink_file(buffer);
532 return;
533 }
534
535 if ((infile = LMAPI->open_file(buffer,"r")) == NULL) {
536 printf("<P><font size=+2><b>Unable to display textfile!</b></font></P>\n");
537 printf("<!-- Error on file: %s -->\n", buffer);
538 LMAPI->unlink_file(buffer);
539 return;
540 }
541
542 printf("<pre>");
543 while((inchar = fgetc(infile)) != EOF) {
544 if ((char)inchar == '<') printf("<");
545 else if ((char)inchar == '>') printf(">");
546 else printf("%c", (char)inchar);
547 }
548 printf("</pre>");
549
550 LMAPI->close_file(infile);
551 LMAPI->unlink_file(buffer);
552 }
553
lsg2_validate(char * buffer,int length)554 int lsg2_validate(char *buffer, int length)
555 {
556 const char *fromaddy;
557 const char *pass;
558
559 fromaddy = LMAPI->get_var("lcgi-user");
560 pass = LMAPI->get_var("lcgi-pass");
561
562 if (!fromaddy) {
563 lsg2_internal_error("No username provided!");
564 return -1;
565 }
566 if (!pass || (strcasecmp(LMAPI->get_string("lcgi-mode"),"passwd") == 0)) {
567
568 if (strcasecmp(LMAPI->get_string("lcgi-mode"),"passwd") == 0) {
569 if (!LMAPI->get_var("lcgi-cookie")) {
570 lsg2_internal_error("Your template did not provide a cookie with a password change. Please have your admin update their templates.");
571 return 0;
572 }
573 }
574
575 switch(lsg2_update_cookie(buffer, length)) {
576 case 1:
577 return 1;
578 case 2:
579 if (!LMAPI->cgi_unparse_template("logincookie")) {
580 lsg2_internal_error("No login cookie template.");
581 }
582 return 0;
583 break;
584 default:
585 return 0;
586 }
587 } else {
588 if (LMAPI->find_pass(fromaddy)) {
589 if (LMAPI->check_pass(fromaddy, pass)) {
590 switch(lsg2_update_cookie(buffer, length)) {
591 case 1:
592 return 1;
593 case 2:
594 if (LMAPI->cgi_unparse_template("logincookie")) {
595 lsg2_internal_error("No login cookie template.");
596 }
597 return 0;
598 break;
599 default:
600 return 0;
601 }
602 } else {
603 lsg2_internal_error("Incorrect password.");
604 return 0;
605 }
606 } else {
607 lsg2_internal_error("No password set for user. Provide blank password and check e-mail for validation.");
608 return 0;
609 }
610 }
611 }
612