base64_decode() with heimdal libs, so I've renamed it base64_decode_inplace().
[ira/wip.git] / source3 / web / cgi.c
index 62a5e71e0516a1c8979891c5427b13c6da528cf0..8abc2f0bd5c6b5ff20b6b1972d92c92eacd6a7fd 100644 (file)
 
 
 #include "includes.h"
-#include "smb.h"
+#include "../web/swat_proto.h"
 
 #define MAX_VARIABLES 10000
 
 /* set the expiry on fixed pages */
 #define EXPIRY_TIME (60*60*24*7)
 
-#define CGI_LOGGING 0
-
 #ifdef DEBUG_COMMENTS
 extern void print_title(char *fmt, ...);
 #endif
@@ -42,61 +40,31 @@ static int num_variables;
 static int content_length;
 static int request_post;
 static char *query_string;
-static char *baseurl;
+static const char *baseurl;
 static char *pathinfo;
 static char *C_user;
 static BOOL inetd_server;
 static BOOL got_request;
 
-static void unescape(char *buf)
-{
-       char *p=buf;
-
-       while ((p=strchr(p,'+')))
-               *p = ' ';
-
-       p = buf;
-
-       while (p && *p && (p=strchr(p,'%'))) {
-               int c1 = p[1];
-               int c2 = p[2];
-
-               if (c1 >= '0' && c1 <= '9')
-                       c1 = c1 - '0';
-               else if (c1 >= 'A' && c1 <= 'F')
-                       c1 = 10 + c1 - 'A';
-               else if (c1 >= 'a' && c1 <= 'f')
-                       c1 = 10 + c1 - 'a';
-               else {p++; continue;}
-
-               if (c2 >= '0' && c2 <= '9')
-                       c2 = c2 - '0';
-               else if (c2 >= 'A' && c2 <= 'F')
-                       c2 = 10 + c2 - 'A';
-               else if (c2 >= 'a' && c2 <= 'f')
-                       c2 = 10 + c2 - 'a';
-               else {p++; continue;}
-                       
-               *p = (c1<<4) | c2;
-
-               memcpy(p+1, p+3, strlen(p+3)+1);
-               p++;
-       }
-}
-
-
 static char *grab_line(FILE *f, int *cl)
 {
-       char *ret;
+       char *ret = NULL;
        int i = 0;
-       int len = 1024;
-
-       ret = (char *)malloc(len);
-       if (!ret) return NULL;
-       
+       int len = 0;
 
        while ((*cl)) {
-               int c = fgetc(f);
+               int c;
+       
+               if (i == len) {
+                       char *ret2;
+                       if (len == 0) len = 1024;
+                       else len *= 2;
+                       ret2 = (char *)Realloc(ret, len);
+                       if (!ret2) return ret;
+                       ret = ret2;
+               }
+       
+               c = fgetc(f);
                (*cl)--;
 
                if (c == EOF) {
@@ -106,17 +74,10 @@ static char *grab_line(FILE *f, int *cl)
                
                if (c == '\r') continue;
 
-               if (strchr("\n&", c)) break;
+               if (strchr_m("\n&", c)) break;
 
                ret[i++] = c;
 
-               if (i == len-1) {
-                       char *ret2;
-                       ret2 = (char *)realloc(ret, len*2);
-                       if (!ret2) return ret;
-                       len *= 2;
-                       ret = ret2;
-               }
        }
        
 
@@ -129,40 +90,33 @@ static char *grab_line(FILE *f, int *cl)
   with the same name and the same or different values. Takes a file parameter
   for simulating CGI invocation eg loading saved preferences.
   ***************************************************************************/
-void cgi_load_variables(FILE *f1)
+void cgi_load_variables(void)
 {
-       FILE *f = f1;
        static char *line;
        char *p, *s, *tok;
-       int len;
+       int len, i;
+       FILE *f = stdin;
 
 #ifdef DEBUG_COMMENTS
        char dummy[100]="";
        print_title(dummy);
-       printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
+       d_printf("<!== Start dump in cgi_load_variables() %s ==>\n",__FILE__);
 #endif
 
-       if (!f1) {
-               f = stdin;
-               if (!content_length) {
-                       p = getenv("CONTENT_LENGTH");
-                       len = p?atoi(p):0;
-               } else {
-                       len = content_length;
-               }
+       if (!content_length) {
+               p = getenv("CONTENT_LENGTH");
+               len = p?atoi(p):0;
        } else {
-               fseek(f, 0, SEEK_END);
-               len = ftell(f);
-               fseek(f, 0, SEEK_SET);
+               len = content_length;
        }
 
 
        if (len > 0 && 
-           (f1 || request_post ||
+           (request_post ||
             ((s=getenv("REQUEST_METHOD")) && 
              strcasecmp(s,"POST")==0))) {
                while (len && (line=grab_line(f, &len))) {
-                       p = strchr(line,'=');
+                       p = strchr_m(line,'=');
                        if (!p) continue;
                        
                        *p = 0;
@@ -170,14 +124,14 @@ void cgi_load_variables(FILE *f1)
                        variables[num_variables].name = strdup(line);
                        variables[num_variables].value = strdup(p+1);
 
-                       free(line);
+                       SAFE_FREE(line);
                        
                        if (!variables[num_variables].name || 
                            !variables[num_variables].value)
                                continue;
 
-                       unescape(variables[num_variables].value);
-                       unescape(variables[num_variables].name);
+                       rfc1738_unescape(variables[num_variables].value);
+                       rfc1738_unescape(variables[num_variables].name);
 
 #ifdef DEBUG_COMMENTS
                        printf("<!== POST var %s has value \"%s\"  ==>\n",
@@ -190,18 +144,12 @@ void cgi_load_variables(FILE *f1)
                }
        }
 
-       if (f1) {
-#ifdef DEBUG_COMMENTS
-               printf("<!== End dump in cgi_load_variables() ==>\n"); 
-#endif
-               return;
-       }
-
        fclose(stdin);
+       open("/dev/null", O_RDWR);
 
        if ((s=query_string) || (s=getenv("QUERY_STRING"))) {
                for (tok=strtok(s,"&;");tok;tok=strtok(NULL,"&;")) {
-                       p = strchr(tok,'=');
+                       p = strchr_m(tok,'=');
                        if (!p) continue;
                        
                        *p = 0;
@@ -213,8 +161,8 @@ void cgi_load_variables(FILE *f1)
                            !variables[num_variables].value)
                                continue;
 
-                       unescape(variables[num_variables].value);
-                       unescape(variables[num_variables].name);
+                       rfc1738_unescape(variables[num_variables].value);
+                       rfc1738_unescape(variables[num_variables].name);
 
 #ifdef DEBUG_COMMENTS
                         printf("<!== Commandline var %s has value \"%s\"  ==>\n",
@@ -229,6 +177,24 @@ void cgi_load_variables(FILE *f1)
 #ifdef DEBUG_COMMENTS
         printf("<!== End dump in cgi_load_variables() ==>\n");   
 #endif
+
+       /* variables from the client are in display charset - convert them
+          to our internal charset before use */
+       for (i=0;i<num_variables;i++) {
+               pstring dest;
+
+               convert_string(CH_DISPLAY, CH_UNIX, 
+                              variables[i].name, -1, 
+                              dest, sizeof(dest));
+               free(variables[i].name);
+               variables[i].name = strdup(dest);
+
+               convert_string(CH_DISPLAY, CH_UNIX, 
+                              variables[i].value, -1,
+                              dest, sizeof(dest));
+               free(variables[i].value);
+               variables[i].value = strdup(dest);
+       }
 }
 
 
@@ -239,7 +205,7 @@ void cgi_load_variables(FILE *f1)
   browser. Also doesn't allow for variables[] containing multiple variables
   with the same name and the same or different values.
   ***************************************************************************/
-char *cgi_variable(char *name)
+const char *cgi_variable(const char *name)
 {
        int i;
 
@@ -252,7 +218,7 @@ char *cgi_variable(char *name)
 /***************************************************************************
 tell a browser about a fatal error in the http processing
   ***************************************************************************/
-static void cgi_setup_error(char *err, char *header, char *info)
+static void cgi_setup_error(const char *err, const char *header, const char *info)
 {
        if (!got_request) {
                /* damn browsers don't like getting cut off before they give a request */
@@ -266,7 +232,7 @@ static void cgi_setup_error(char *err, char *header, char *info)
                }
        }
 
-       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);
+       d_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);
        fclose(stdin);
        fclose(stdout);
        exit(0);
@@ -293,37 +259,36 @@ static void cgi_auth_error(void)
        exit(0);
 }
 
-
 /***************************************************************************
-decode a base64 string in-place - simple and slow algorithm
+authenticate when we are running as a CGI
   ***************************************************************************/
-static void base64_decode(char *s)
+static void cgi_web_auth(void)
 {
-       char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-       int bit_offset, byte_offset, idx, i, n;
-       unsigned char *d = (unsigned char *)s;
-       char *p;
+       const char *user = getenv("REMOTE_USER");
+       struct passwd *pwd;
+       const char *head = "Content-Type: text/html\r\n\r\n<HTML><BODY><H1>SWAT installation Error</H1>\n";
+       const char *tail = "</BODY></HTML>\r\n";
 
-       n=i=0;
+       if (!user) {
+               printf("%sREMOTE_USER not set. Not authenticated by web server.<br>%s\n",
+                      head, tail);
+               exit(0);
+       }
 
-       while (*s && (p=strchr(b64,*s))) {
-               idx = (int)(p - b64);
-               byte_offset = (i*6)/8;
-               bit_offset = (i*6)%8;
-               d[byte_offset] &= ~((1<<(8-bit_offset))-1);
-               if (bit_offset < 3) {
-                       d[byte_offset] |= (idx << (2-bit_offset));
-                       n = byte_offset+1;
-               } else {
-                       d[byte_offset] |= (idx >> (bit_offset-2));
-                       d[byte_offset+1] = 0;
-                       d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
-                       n = byte_offset+2;
-               }
-               s++; i++;
+       pwd = getpwnam_alloc(user);
+       if (!pwd) {
+               printf("%sCannot find user %s<br>%s\n", head, user, tail);
+               exit(0);
+       }
+
+       setuid(0);
+       setuid(pwd->pw_uid);
+       if (geteuid() != pwd->pw_uid || getuid() != pwd->pw_uid) {
+               printf("%sFailed to become user %s - uid=%d/%d<br>%s\n", 
+                      head, user, (int)geteuid(), (int)getuid(), tail);
+               exit(0);
        }
-       /* null terminate */
-       d[n] = 0;
+       passwd_free(&pwd);
 }
 
 
@@ -332,68 +297,66 @@ handle a http authentication line
   ***************************************************************************/
 static BOOL cgi_handle_authorization(char *line)
 {
-       char *p, *user, *user_pass;
+       char *p;
+       fstring user, user_pass;
        struct passwd *pass = NULL;
-       BOOL ret = False;
 
        if (strncasecmp(line,"Basic ", 6)) {
-               cgi_setup_error("401 Bad Authorization", "", 
-                               "Only basic authorization is understood");
-               return False;
+               goto err;
        }
        line += 6;
        while (line[0] == ' ') line++;
-       base64_decode(line);
-       if (!(p=strchr(line,':'))) {
+       base64_decode_inplace(line);
+       if (!(p=strchr_m(line,':'))) {
                /*
                 * Always give the same error so a cracker
                 * cannot tell why we fail.
                 */
-               cgi_setup_error("401 Bad Authorization", "", 
-                               "username/password must be supplied");
-               return False;
+               goto err;
        }
        *p = 0;
-       user = line;
-       user_pass = p+1;
+
+       convert_string(CH_DISPLAY, CH_UNIX, 
+                      line, -1, 
+                      user, sizeof(user));
+
+       convert_string(CH_DISPLAY, CH_UNIX, 
+                      p+1, -1, 
+                      user_pass, sizeof(user_pass));
 
        /*
         * Try and get the user from the UNIX password file.
         */
-
-       if(!(pass = Get_Pwnam(user,False))) {
-               /*
-                * Always give the same error so a cracker
-                * cannot tell why we fail.
-                */
-               cgi_setup_error("401 Bad Authorization", "",
-                               "username/password must be supplied");
-               return False;
-       }
-
+       
+       pass = getpwnam_alloc(user);
+       
        /*
         * Validate the password they have given.
         */
-
-       if((ret = pass_check(user, user_pass, strlen(user_pass), NULL, NULL)) == True) {
-
-               /*
-                * Password was ok.
-                */
-
-               if(pass->pw_uid != 0) {
+       
+       if NT_STATUS_IS_OK(pass_check(pass, user, user_pass, 
+                     strlen(user_pass), NULL, False)) {
+               
+               if (pass) {
                        /*
-                        * We have not authenticated as root,
-                        * become the user *permanently*.
+                        * Password was ok.
                         */
+                       
                        become_user_permanently(pass->pw_uid, pass->pw_gid);
+                       
+                       /* Save the users name */
+                       C_user = strdup(user);
+                       passwd_free(&pass);
+                       return True;
                }
-
-               /* Save the users name */
-               C_user = strdup(user);
        }
+       
+err:
+       cgi_setup_error("401 Bad Authorization", "", 
+                       "username or password incorrect");
 
-       return ret;
+       passwd_free(&pass);
+       return False;
 }
 
 /***************************************************************************
@@ -426,10 +389,11 @@ static void cgi_download(char *file)
        char buf[1024];
        int fd, l, i;
        char *p;
+       char *lang;
 
        /* sanitise the filename */
        for (i=0;file[i];i++) {
-               if (!isalnum((int)file[i]) && !strchr("/.-_", file[i])) {
+               if (!isalnum((int)file[i]) && !strchr_m("/.-_", file[i])) {
                        cgi_setup_error("404 File Not Found","",
                                        "Illegal character in filename");
                }
@@ -439,23 +403,31 @@ static void cgi_download(char *file)
                cgi_setup_error("404 File Not Found","",
                                "The requested file was not found");
        }
-       fd = sys_open(file,O_RDONLY,0);
+
+       fd = web_open(file,O_RDONLY,0);
        if (fd == -1) {
                cgi_setup_error("404 File Not Found","",
                                "The requested file was not found");
        }
        printf("HTTP/1.0 200 OK\r\n");
-       if ((p=strrchr(file,'.'))) {
+       if ((p=strrchr_m(file,'.'))) {
                if (strcmp(p,".gif")==0) {
                        printf("Content-Type: image/gif\r\n");
                } else if (strcmp(p,".jpg")==0) {
                        printf("Content-Type: image/jpeg\r\n");
+               } else if (strcmp(p,".txt")==0) {
+                       printf("Content-Type: text/plain\r\n");
                } else {
                        printf("Content-Type: text/html\r\n");
                }
        }
        printf("Expires: %s\r\n", http_timestring(time(NULL)+EXPIRY_TIME));
 
+       lang = lang_tdb_current();
+       if (lang) {
+               printf("Content-Language: %s\r\n", lang);
+       }
+
        printf("Content-Length: %d\r\n\r\n", (int)st.st_size);
        while ((l=read(fd,buf,sizeof(buf)))>0) {
                fwrite(buf, 1, l, stdout);
@@ -465,29 +437,40 @@ static void cgi_download(char *file)
 }
 
 
-/***************************************************************************
-setup the cgi framework, handling the possability that this program is either
-run as a true cgi program by a web browser or is itself a mini web server
-  ***************************************************************************/
-void cgi_setup(char *rootdir, int auth_required)
+
+
+/**
+ * @brief Setup the CGI framework.
+ *
+ * Setup the cgi framework, handling the possibility that this program
+ * is either run as a true CGI program with a gateway to a web server, or
+ * is itself a mini web server.
+ **/
+void cgi_setup(const char *rootdir, int auth_required)
 {
        BOOL authenticated = False;
        char line[1024];
        char *url=NULL;
        char *p;
-#if CGI_LOGGING
-       FILE *f;
-#endif
+       char *lang;
 
        if (chdir(rootdir)) {
                cgi_setup_error("400 Server Error", "",
                                "chdir failed - the server is not configured correctly");
        }
 
+       /* Handle the possability we might be running as non-root */
+       sec_init();
+
+       if ((lang=getenv("HTTP_ACCEPT_LANGUAGE"))) {
+               /* if running as a cgi program */
+               web_set_lang(lang);
+       }
+
        /* maybe we are running under a web server */
        if (getenv("CONTENT_LENGTH") || getenv("REQUEST_METHOD")) {
                if (auth_required) {
-                       cgi_auth_error();
+                       cgi_web_auth();
                }
                return;
        }
@@ -499,19 +482,9 @@ void cgi_setup(char *rootdir, int auth_required)
                                "Samba is configured to deny access from this client\n<br>Check your \"hosts allow\" and \"hosts deny\" options in smb.conf ");
        }
 
-#if CGI_LOGGING
-       f = sys_fopen("/tmp/cgi.log", "a");
-       if (f) fprintf(f,"\n[Date: %s   %s (%s)]\n", 
-                      http_timestring(time(NULL)),
-                      client_name(1), client_addr(1));
-#endif
-
        /* we are a mini-web server. We need to read the request from stdin
           and handle authentication etc */
        while (fgets(line, sizeof(line)-1, stdin)) {
-#if CGI_LOGGING
-               if (f) fputs(line, f);
-#endif
                if (line[0] == '\r' || line[0] == '\n') break;
                if (strncasecmp(line,"GET ", 4)==0) {
                        got_request = True;
@@ -528,12 +501,11 @@ void cgi_setup(char *rootdir, int auth_required)
                        authenticated = cgi_handle_authorization(&line[15]);
                } else if (strncasecmp(line,"Content-Length: ", 16)==0) {
                        content_length = atoi(&line[16]);
+               } else if (strncasecmp(line,"Accept-Language: ", 17)==0) {
+                       web_set_lang(&line[17]);
                }
                /* ignore all other requests! */
        }
-#if CGI_LOGGING
-       if (f) fclose(f);
-#endif
 
        if (auth_required && !authenticated) {
                cgi_auth_error();
@@ -545,15 +517,15 @@ void cgi_setup(char *rootdir, int auth_required)
        }
 
        /* trim the URL */
-       if ((p = strchr(url,' ')) || (p=strchr(url,'\t'))) {
+       if ((p = strchr_m(url,' ')) || (p=strchr_m(url,'\t'))) {
                *p = 0;
        }
-       while (*url && strchr("\r\n",url[strlen(url)-1])) {
+       while (*url && strchr_m("\r\n",url[strlen(url)-1])) {
                url[strlen(url)-1] = 0;
        }
 
        /* anything following a ? in the URL is part of the query string */
-       if ((p=strchr(url,'?'))) {
+       if ((p=strchr_m(url,'?'))) {
                query_string = p+1;
                *p = 0;
        }
@@ -574,7 +546,7 @@ void cgi_setup(char *rootdir, int auth_required)
 /***************************************************************************
 return the current pages URL
   ***************************************************************************/
-char *cgi_baseurl(void)
+const char *cgi_baseurl(void)
 {
        if (inetd_server) {
                return baseurl;
@@ -585,7 +557,7 @@ char *cgi_baseurl(void)
 /***************************************************************************
 return the current pages path info
   ***************************************************************************/
-char *cgi_pathinfo(void)
+const char *cgi_pathinfo(void)
 {
        char *r;
        if (inetd_server) {
@@ -603,7 +575,7 @@ return the hostname of the client
 char *cgi_remote_host(void)
 {
        if (inetd_server) {
-               return client_name(1);
+               return get_socket_name(1,False);
        }
        return getenv("REMOTE_HOST");
 }
@@ -614,7 +586,7 @@ return the hostname of the client
 char *cgi_remote_addr(void)
 {
        if (inetd_server) {
-               return client_addr(1);
+               return get_socket_addr(1);
        }
        return getenv("REMOTE_ADDR");
 }