b97ed2578c5bdc6abd9e76203cdcdb1a560599f3
[samba.git] / source3 / web / cgi.c
1 /* 
2    some simple CGI helper routines
3    Copyright (C) Andrew Tridgell 1997-1998
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19
20 #include "includes.h"
21 #include "system/passwd.h"
22 #include "system/filesys.h"
23 #include "web/swat_proto.h"
24 #include "intl/lang_tdb.h"
25 #include "auth.h"
26 #include "secrets.h"
27 #include "../lib/util/setid.h"
28
29 #define MAX_VARIABLES 10000
30
31 /* set the expiry on fixed pages */
32 #define EXPIRY_TIME (60*60*24*7)
33
34 #ifdef DEBUG_COMMENTS
35 extern void print_title(char *fmt, ...);
36 #endif
37
38 struct cgi_var {
39         char *name;
40         char *value;
41 };
42
43 static struct cgi_var variables[MAX_VARIABLES];
44 static int num_variables;
45 static int content_length;
46 static int request_post;
47 static char *query_string;
48 static const char *baseurl;
49 static char *pathinfo;
50 static char *C_user;
51 static char *C_pass;
52 static bool inetd_server;
53 static bool got_request;
54
55 static char *grab_line(FILE *f, int *cl)
56 {
57         char *ret = NULL;
58         int i = 0;
59         int len = 0;
60
61         while ((*cl)) {
62                 int c;
63
64                 if (i == len) {
65                         char *ret2;
66                         if (len == 0) len = 1024;
67                         else len *= 2;
68                         ret2 = (char *)SMB_REALLOC_KEEP_OLD_ON_ERROR(ret, len);
69                         if (!ret2) return ret;
70                         ret = ret2;
71                 }
72
73                 c = fgetc(f);
74                 (*cl)--;
75
76                 if (c == EOF) {
77                         (*cl) = 0;
78                         break;
79                 }
80
81                 if (c == '\r') continue;
82
83                 if (strchr_m("\n&", c)) break;
84
85                 ret[i++] = c;
86
87         }
88
89         if (ret) {
90                 ret[i] = 0;
91         }
92         return ret;
93 }
94
95 /**
96  URL encoded strings can have a '+', which should be replaced with a space
97
98  (This was in rfc1738_unescape(), but that broke the squid helper)
99 **/
100
101 static void plus_to_space_unescape(char *buf)
102 {
103         char *p=buf;
104
105         while ((p=strchr_m(p,'+')))
106                 *p = ' ';
107 }
108
109 /***************************************************************************
110   load all the variables passed to the CGI program. May have multiple variables
111   with the same name and the same or different values. Takes a file parameter
112   for simulating CGI invocation eg loading saved preferences.
113   ***************************************************************************/
114 void cgi_load_variables(void)
115 {
116         static char *line;
117         char *p, *s, *tok;
118         int len, i;
119         FILE *f = stdin;
120
121 #ifdef DEBUG_COMMENTS
122         char dummy[100]="";
123         print_title(dummy);
124         printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
125 #endif
126
127         if (!content_length) {
128                 p = getenv("CONTENT_LENGTH");
129                 len = p?atoi(p):0;
130         } else {
131                 len = content_length;
132         }
133
134
135         if (len > 0 && 
136             (request_post ||
137              ((s=getenv("REQUEST_METHOD")) && 
138               strequal(s,"POST")))) {
139                 while (len && (line=grab_line(f, &len))) {
140                         p = strchr_m(line,'=');
141                         if (!p) continue;
142
143                         *p = 0;
144
145                         variables[num_variables].name = SMB_STRDUP(line);
146                         variables[num_variables].value = SMB_STRDUP(p+1);
147
148                         SAFE_FREE(line);
149
150                         if (!variables[num_variables].name || 
151                             !variables[num_variables].value)
152                                 continue;
153
154                         plus_to_space_unescape(variables[num_variables].value);
155                         rfc1738_unescape(variables[num_variables].value);
156                         plus_to_space_unescape(variables[num_variables].name);
157                         rfc1738_unescape(variables[num_variables].name);
158
159 #ifdef DEBUG_COMMENTS
160                         printf("<!== POST var %s has value \"%s\"  ==>\n",
161                                variables[num_variables].name,
162                                variables[num_variables].value);
163 #endif
164
165                         num_variables++;
166                         if (num_variables == MAX_VARIABLES) break;
167                 }
168         }
169
170         fclose(stdin);
171         open("/dev/null", O_RDWR);
172
173         if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
174                 char *saveptr;
175                 for (tok=strtok_r(s, "&;", &saveptr); tok;
176                      tok=strtok_r(NULL, "&;", &saveptr)) {
177                         p = strchr_m(tok,'=');
178                         if (!p) continue;
179
180                         *p = 0;
181
182                         variables[num_variables].name = SMB_STRDUP(tok);
183                         variables[num_variables].value = SMB_STRDUP(p+1);
184
185                         if (!variables[num_variables].name ||
186                             !variables[num_variables].value)
187                                 continue;
188
189                         plus_to_space_unescape(variables[num_variables].value);
190                         rfc1738_unescape(variables[num_variables].value);
191                         plus_to_space_unescape(variables[num_variables].name);
192                         rfc1738_unescape(variables[num_variables].name);
193
194 #ifdef DEBUG_COMMENTS
195                         printf("<!== Commandline var %s has value \"%s\"  ==>\n",
196                                variables[num_variables].name,
197                                variables[num_variables].value);
198 #endif
199                         num_variables++;
200                         if (num_variables == MAX_VARIABLES) break;
201                 }
202
203         }
204 #ifdef DEBUG_COMMENTS
205         printf("<!== End dump in cgi_load_variables() ==>\n");
206 #endif
207
208         /* variables from the client are in UTF-8 - convert them
209            to our internal unix charset before use */
210         for (i=0;i<num_variables;i++) {
211                 TALLOC_CTX *frame = talloc_stackframe();
212                 char *dest = NULL;
213                 size_t dest_len;
214
215                 convert_string_talloc(frame, CH_UTF8, CH_UNIX,
216                                variables[i].name, strlen(variables[i].name),
217                                &dest, &dest_len);
218                 SAFE_FREE(variables[i].name);
219                 variables[i].name = SMB_STRDUP(dest ? dest : "");
220
221                 dest = NULL;
222                 convert_string_talloc(frame, CH_UTF8, CH_UNIX,
223                                variables[i].value, strlen(variables[i].value),
224                                &dest, &dest_len);
225                 SAFE_FREE(variables[i].value);
226                 variables[i].value = SMB_STRDUP(dest ? dest : "");
227                 TALLOC_FREE(frame);
228         }
229 }
230
231
232 /***************************************************************************
233   find a variable passed via CGI
234   Doesn't quite do what you think in the case of POST text variables, because
235   if they exist they might have a value of "" or even " ", depending on the
236   browser. Also doesn't allow for variables[] containing multiple variables
237   with the same name and the same or different values.
238   ***************************************************************************/
239
240 const char *cgi_variable(const char *name)
241 {
242         int i;
243
244         for (i=0;i<num_variables;i++)
245                 if (strcmp(variables[i].name, name) == 0)
246                         return variables[i].value;
247         return NULL;
248 }
249
250 /***************************************************************************
251  Version of the above that can't return a NULL pointer.
252 ***************************************************************************/
253
254 const char *cgi_variable_nonull(const char *name)
255 {
256         const char *var = cgi_variable(name);
257         if (var) {
258                 return var;
259         } else {
260                 return "";
261         }
262 }
263
264 /***************************************************************************
265 tell a browser about a fatal error in the http processing
266   ***************************************************************************/
267 static void cgi_setup_error(const char *err, const char *header, const char *info)
268 {
269         if (!got_request) {
270                 /* damn browsers don't like getting cut off before they give a request */
271                 char line[1024];
272                 while (fgets(line, sizeof(line)-1, stdin)) {
273                         if (strnequal(line,"GET ", 4) || 
274                             strnequal(line,"POST ", 5) ||
275                             strnequal(line,"PUT ", 4)) {
276                                 break;
277                         }
278                 }
279         }
280
281         printf("HTTP/1.0 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>%s</H1>%s<p></BODY></HTML>\r\n\r\n", err, header, err, err, info);
282         fclose(stdin);
283         fclose(stdout);
284         exit(0);
285 }
286
287
288 /***************************************************************************
289 tell a browser about a fatal authentication error
290   ***************************************************************************/
291 static void cgi_auth_error(void)
292 {
293         if (inetd_server) {
294                 cgi_setup_error("401 Authorization Required", 
295                                 "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
296                                 "You must be authenticated to use this service");
297         } else {
298                 printf("Content-Type: text/html\r\n");
299
300                 printf("\r\n<HTML><HEAD><TITLE>SWAT</TITLE></HEAD>\n");
301                 printf("<BODY><H1>Installation Error</H1>\n");
302                 printf("SWAT must be installed via inetd. It cannot be run as a CGI script<p>\n");
303                 printf("</BODY></HTML>\r\n");
304         }
305         exit(0);
306 }
307
308 /***************************************************************************
309 authenticate when we are running as a CGI
310   ***************************************************************************/
311 static void cgi_web_auth(void)
312 {
313         const char *user = getenv("REMOTE_USER");
314         struct passwd *pwd;
315         const char *head = "Content-Type: text/html\r\n\r\n<HTML><BODY><H1>SWAT installation Error</H1>\n";
316         const char *tail = "</BODY></HTML>\r\n";
317
318         if (!user) {
319                 printf("%sREMOTE_USER not set. Not authenticated by web server.<br>%s\n",
320                        head, tail);
321                 exit(0);
322         }
323
324         pwd = Get_Pwnam_alloc(talloc_tos(), user);
325         if (!pwd) {
326                 printf("%sCannot find user %s<br>%s\n", head, user, tail);
327                 exit(0);
328         }
329
330         C_user = SMB_STRDUP(user);
331
332         if (!samba_setuid(0)) {
333                 C_pass = secrets_fetch_generic("root", "SWAT");
334                 if (C_pass == NULL) {
335                         char *tmp_pass = NULL;
336                         tmp_pass = generate_random_password(talloc_tos(),
337                                                             16, 16);
338                         if (tmp_pass == NULL) {
339                                 printf("%sFailed to create random nonce for "
340                                        "SWAT session\n<br>%s\n", head, tail);
341                                 exit(0);
342                         }
343                         secrets_store_generic("root", "SWAT", tmp_pass);
344                         C_pass = SMB_STRDUP(tmp_pass);
345                         TALLOC_FREE(tmp_pass);
346                 }
347         }
348         samba_setuid(pwd->pw_uid);
349         if (geteuid() != pwd->pw_uid || getuid() != pwd->pw_uid) {
350                 printf("%sFailed to become user %s - uid=%d/%d<br>%s\n", 
351                        head, user, (int)geteuid(), (int)getuid(), tail);
352                 exit(0);
353         }
354         TALLOC_FREE(pwd);
355 }
356
357
358 /***************************************************************************
359 handle a http authentication line
360   ***************************************************************************/
361 static bool cgi_handle_authorization(char *line)
362 {
363         char *p;
364         fstring user, user_pass;
365         struct passwd *pass = NULL;
366         const char *rhost;
367         char addr[INET6_ADDRSTRLEN];
368         size_t size = 0;
369
370         if (!strnequal(line,"Basic ", 6)) {
371                 goto err;
372         }
373         line += 6;
374         while (line[0] == ' ') line++;
375         base64_decode_inplace(line);
376         if (!(p=strchr_m(line,':'))) {
377                 /*
378                  * Always give the same error so a cracker
379                  * cannot tell why we fail.
380                  */
381                 goto err;
382         }
383         *p = 0;
384
385         if (!convert_string(CH_UTF8, CH_UNIX,
386                        line, -1, 
387                        user, sizeof(user), &size)) {
388                 goto err;
389         }
390
391         if (!convert_string(CH_UTF8, CH_UNIX,
392                        p+1, -1, 
393                        user_pass, sizeof(user_pass), &size)) {
394                 goto err;
395         }
396
397         /*
398          * Try and get the user from the UNIX password file.
399          */
400
401         pass = Get_Pwnam_alloc(talloc_tos(), user);
402
403         rhost = client_name(1);
404         if (strequal(rhost,"UNKNOWN"))
405                 rhost = client_addr(1, addr, sizeof(addr));
406
407         /*
408          * Validate the password they have given.
409          */
410
411         if NT_STATUS_IS_OK(pass_check(pass, user, rhost, user_pass, false)) {
412                 if (pass) {
413                         /*
414                          * Password was ok.
415                          */
416
417                         if ( initgroups(pass->pw_name, pass->pw_gid) != 0 )
418                                 goto err;
419
420                         become_user_permanently(pass->pw_uid, pass->pw_gid);
421
422                         /* Save the users name */
423                         C_user = SMB_STRDUP(user);
424                         C_pass = SMB_STRDUP(user_pass);
425                         TALLOC_FREE(pass);
426                         return True;
427                 }
428         }
429
430 err:
431         cgi_setup_error("401 Bad Authorization", 
432                         "WWW-Authenticate: Basic realm=\"SWAT\"\r\n",
433                         "username or password incorrect");
434
435         TALLOC_FREE(pass);
436         return False;
437 }
438
439 /***************************************************************************
440 is this root?
441   ***************************************************************************/
442 bool am_root(void)
443 {
444         if (geteuid() == 0) {
445                 return( True);
446         } else {
447                 return( False);
448         }
449 }
450
451 /***************************************************************************
452 return a ptr to the users name
453   ***************************************************************************/
454 char *cgi_user_name(void)
455 {
456         return(C_user);
457 }
458
459 /***************************************************************************
460 return a ptr to the users password
461   ***************************************************************************/
462 char *cgi_user_pass(void)
463 {
464         return(C_pass);
465 }
466
467 /***************************************************************************
468 handle a file download
469   ***************************************************************************/
470 static void cgi_download(char *file)
471 {
472         SMB_STRUCT_STAT st;
473         char buf[1024];
474         int fd, l, i;
475         char *p;
476         char *lang;
477
478         /* sanitise the filename */
479         for (i=0;file[i];i++) {
480                 if (!isalnum((int)file[i]) && !strchr_m("/.-_", file[i])) {
481                         cgi_setup_error("404 File Not Found","",
482                                         "Illegal character in filename");
483                 }
484         }
485
486         if (sys_stat(file, &st, false) != 0)    {
487                 cgi_setup_error("404 File Not Found","",
488                                 "The requested file was not found");
489         }
490
491         if (S_ISDIR(st.st_ex_mode))
492         {
493                 snprintf(buf, sizeof(buf), "%s/index.html", file);
494                 if (!file_exist_stat(buf, &st, false)
495                     || !S_ISREG(st.st_ex_mode))
496                 {
497                         cgi_setup_error("404 File Not Found","",
498                                         "The requested file was not found");
499                 }
500         }
501         else if (S_ISREG(st.st_ex_mode))
502         {
503                 snprintf(buf, sizeof(buf), "%s", file);
504         }
505         else
506         {
507                 cgi_setup_error("404 File Not Found","",
508                                 "The requested file was not found");
509         }
510
511         fd = web_open(buf,O_RDONLY,0);
512         if (fd == -1) {
513                 cgi_setup_error("404 File Not Found","",
514                                 "The requested file was not found");
515         }
516         printf("HTTP/1.0 200 OK\r\n");
517         if ((p=strrchr_m(buf, '.'))) {
518                 if (strcmp(p,".gif")==0) {
519                         printf("Content-Type: image/gif\r\n");
520                 } else if (strcmp(p,".jpg")==0) {
521                         printf("Content-Type: image/jpeg\r\n");
522                 } else if (strcmp(p,".png")==0) {
523                         printf("Content-Type: image/png\r\n");
524                 } else if (strcmp(p,".css")==0) {
525                         printf("Content-Type: text/css\r\n");
526                 } else if (strcmp(p,".txt")==0) {
527                         printf("Content-Type: text/plain\r\n");
528                 } else {
529                         printf("Content-Type: text/html\r\n");
530                 }
531         }
532         printf("Expires: %s\r\n", 
533                    http_timestring(talloc_tos(), time(NULL)+EXPIRY_TIME));
534
535         lang = lang_tdb_current();
536         if (lang) {
537                 printf("Content-Language: %s\r\n", lang);
538         }
539
540         printf("Content-Length: %d\r\n\r\n", (int)st.st_ex_size);
541         while ((l=read(fd,buf,sizeof(buf)))>0) {
542                 if (fwrite(buf, 1, l, stdout) != l) {
543                         break;
544                 }
545         }
546         close(fd);
547         exit(0);
548 }
549
550
551
552 /* return true if the char* contains ip addrs only.  Used to avoid
553 name lookup calls */
554
555 static bool only_ipaddrs_in_list(const char **list)
556 {
557         bool only_ip = true;
558
559         if (!list) {
560                 return true;
561         }
562
563         for (; *list ; list++) {
564                 /* factor out the special strings */
565                 if (strequal(*list, "ALL") || strequal(*list, "FAIL") ||
566                     strequal(*list, "EXCEPT")) {
567                         continue;
568                 }
569
570                 if (!is_ipaddress(*list)) {
571                         /*
572                          * If we failed, make sure that it was not because
573                          * the token was a network/netmask pair. Only
574                          * network/netmask pairs have a '/' in them.
575                          */
576                         if ((strchr_m(*list, '/')) == NULL) {
577                                 only_ip = false;
578                                 DEBUG(3,("only_ipaddrs_in_list: list has "
579                                         "non-ip address (%s)\n",
580                                         *list));
581                                 break;
582                         }
583                 }
584         }
585
586         return only_ip;
587 }
588
589 /* return true if access should be allowed to a service for a socket */
590 static bool check_access(int sock, const char **allow_list,
591                          const char **deny_list)
592 {
593         bool ret = false;
594         bool only_ip = false;
595         char addr[INET6_ADDRSTRLEN];
596
597         if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0)) {
598                 return true;
599         }
600
601         /* Bypass name resolution calls if the lists
602          * only contain IP addrs */
603         if (only_ipaddrs_in_list(allow_list) &&
604             only_ipaddrs_in_list(deny_list)) {
605                 only_ip = true;
606                 DEBUG (3, ("check_access: no hostnames "
607                            "in host allow/deny list.\n"));
608                 ret = allow_access(deny_list,
609                                    allow_list,
610                                    "",
611                                    get_peer_addr(sock,addr,sizeof(addr)));
612         } else {
613                 DEBUG (3, ("check_access: hostnames in "
614                            "host allow/deny list.\n"));
615                 ret = allow_access(deny_list,
616                                    allow_list,
617                                    get_peer_name(sock,true),
618                                    get_peer_addr(sock,addr,sizeof(addr)));
619         }
620
621         if (ret) {
622                 DEBUG(2,("Allowed connection from %s (%s)\n",
623                          only_ip ? "" : get_peer_name(sock,true),
624                          get_peer_addr(sock,addr,sizeof(addr))));
625         } else {
626                 DEBUG(0,("Denied connection from %s (%s)\n",
627                          only_ip ? "" : get_peer_name(sock,true),
628                          get_peer_addr(sock,addr,sizeof(addr))));
629         }
630
631         return(ret);
632 }
633
634 /**
635  * @brief Setup the CGI framework.
636  *
637  * Setup the cgi framework, handling the possibility that this program
638  * is either run as a true CGI program with a gateway to a web server, or
639  * is itself a mini web server.
640  **/
641 void cgi_setup(const char *rootdir, int auth_required)
642 {
643         bool authenticated = False;
644         char line[1024];
645         char *url=NULL;
646         char *p;
647         char *lang;
648
649         if (chdir(rootdir)) {
650                 cgi_setup_error("500 Server Error", "",
651                                 "chdir failed - the server is not configured correctly");
652         }
653
654         /* Handle the possibility we might be running as non-root */
655         sec_init();
656
657         if ((lang=getenv("HTTP_ACCEPT_LANGUAGE"))) {
658                 /* if running as a cgi program */
659                 web_set_lang(lang);
660         }
661
662         /* maybe we are running under a web server */
663         if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
664                 if (auth_required) {
665                         cgi_web_auth();
666                 }
667                 return;
668         }
669
670         inetd_server = True;
671
672         if (!check_access(1, lp_hostsallow(-1), lp_hostsdeny(-1))) {
673                 cgi_setup_error("403 Forbidden", "",
674                                 "Samba is configured to deny access from this client\n<br>Check your \"hosts allow\" and \"hosts deny\" options in smb.conf ");
675         }
676
677         /* we are a mini-web server. We need to read the request from stdin
678            and handle authentication etc */
679         while (fgets(line, sizeof(line)-1, stdin)) {
680                 if (line[0] == '\r' || line[0] == '\n') break;
681                 if (strnequal(line,"GET ", 4)) {
682                         got_request = True;
683                         url = SMB_STRDUP(&line[4]);
684                 } else if (strnequal(line,"POST ", 5)) {
685                         got_request = True;
686                         request_post = 1;
687                         url = SMB_STRDUP(&line[5]);
688                 } else if (strnequal(line,"PUT ", 4)) {
689                         got_request = True;
690                         cgi_setup_error("400 Bad Request", "",
691                                         "This server does not accept PUT requests");
692                 } else if (strnequal(line,"Authorization: ", 15)) {
693                         authenticated = cgi_handle_authorization(&line[15]);
694                 } else if (strnequal(line,"Content-Length: ", 16)) {
695                         content_length = atoi(&line[16]);
696                 } else if (strnequal(line,"Accept-Language: ", 17)) {
697                         web_set_lang(&line[17]);
698                 }
699                 /* ignore all other requests! */
700         }
701
702         if (auth_required && !authenticated) {
703                 cgi_auth_error();
704         }
705
706         if (!url) {
707                 cgi_setup_error("400 Bad Request", "",
708                                 "You must specify a GET or POST request");
709         }
710
711         /* trim the URL */
712         if ((p = strchr_m(url,' ')) || (p=strchr_m(url,'\t'))) {
713                 *p = 0;
714         }
715         while (*url && strchr_m("\r\n",url[strlen(url)-1])) {
716                 url[strlen(url)-1] = 0;
717         }
718
719         /* anything following a ? in the URL is part of the query string */
720         if ((p=strchr_m(url,'?'))) {
721                 query_string = p+1;
722                 *p = 0;
723         }
724
725         string_sub(url, "/swat/", "", 0);
726
727         if (url[0] != '/' && strstr(url,"..")==0) {
728                 cgi_download(url);
729         }
730
731         printf("HTTP/1.0 200 OK\r\nConnection: close\r\n");
732         printf("Date: %s\r\n", http_timestring(talloc_tos(), time(NULL)));
733         baseurl = "";
734         pathinfo = url+1;
735 }
736
737
738 /***************************************************************************
739 return the current pages URL
740   ***************************************************************************/
741 const char *cgi_baseurl(void)
742 {
743         if (inetd_server) {
744                 return baseurl;
745         }
746         return getenv("SCRIPT_NAME");
747 }
748
749 /***************************************************************************
750 return the current pages path info
751   ***************************************************************************/
752 const char *cgi_pathinfo(void)
753 {
754         char *r;
755         if (inetd_server) {
756                 return pathinfo;
757         }
758         r = getenv("PATH_INFO");
759         if (!r) return "";
760         if (*r == '/') r++;
761         return r;
762 }
763
764 /***************************************************************************
765 return the hostname of the client
766   ***************************************************************************/
767 const char *cgi_remote_host(void)
768 {
769         if (inetd_server) {
770                 return get_peer_name(1,False);
771         }
772         return getenv("REMOTE_HOST");
773 }
774
775 /***************************************************************************
776 return the hostname of the client
777   ***************************************************************************/
778 const char *cgi_remote_addr(void)
779 {
780         if (inetd_server) {
781                 char addr[INET6_ADDRSTRLEN];
782                 get_peer_addr(1,addr,sizeof(addr));
783                 return talloc_strdup(talloc_tos(), addr);
784         }
785         return getenv("REMOTE_ADDR");
786 }
787
788
789 /***************************************************************************
790 return True if the request was a POST
791   ***************************************************************************/
792 bool cgi_waspost(void)
793 {
794         if (inetd_server) {
795                 return request_post;
796         }
797         return strequal(getenv("REQUEST_METHOD"), "POST");
798 }