s3/swat: use strlcat instead of strncat to fix build on old Linux distros
[samba.git] / source3 / web / swat.c
index 4bfb731814807847171953f688226fda824c8074..754e3cee732fec28583b221581ba8cb118e441d4 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "includes.h"
 #include "web/swat_proto.h"
+#include "../lib/crypto/md5.h"
 
 static int demo_mode = False;
 static int passwd_only = False;
@@ -50,6 +51,9 @@ static int iNumNonAutoPrintServices = 0;
 #define DISABLE_USER_FLAG "disable_user_flag"
 #define ENABLE_USER_FLAG "enable_user_flag"
 #define RHOST "remote_host"
+#define XSRF_TOKEN "xsrf"
+#define XSRF_TIME "xsrf_time"
+#define XSRF_TIMEOUT 300
 
 #define _(x) lang_msg_rotate(talloc_tos(),x)
 
@@ -138,6 +142,89 @@ static char *make_parm_name(const char *label)
        return parmname;
 }
 
+void get_xsrf_token(const char *username, const char *pass,
+                   const char *formname, time_t xsrf_time, char token_str[33])
+{
+       struct MD5Context md5_ctx;
+       uint8_t token[16];
+       int i;
+
+       token_str[0] = '\0';
+       ZERO_STRUCT(md5_ctx);
+       MD5Init(&md5_ctx);
+
+       MD5Update(&md5_ctx, (uint8_t *)formname, strlen(formname));
+       MD5Update(&md5_ctx, (uint8_t *)&xsrf_time, sizeof(time_t));
+       if (username != NULL) {
+               MD5Update(&md5_ctx, (uint8_t *)username, strlen(username));
+       }
+       if (pass != NULL) {
+               MD5Update(&md5_ctx, (uint8_t *)pass, strlen(pass));
+       }
+
+       MD5Final(token, &md5_ctx);
+
+       for(i = 0; i < sizeof(token); i++) {
+               char tmp[3];
+
+               snprintf(tmp, sizeof(tmp), "%02x", token[i]);
+               strlcat(token_str, tmp, sizeof(tmp));
+       }
+}
+
+void print_xsrf_token(const char *username, const char *pass,
+                     const char *formname)
+{
+       char token[33];
+       time_t xsrf_time = time(NULL);
+
+       get_xsrf_token(username, pass, formname, xsrf_time, token);
+       printf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n",
+              XSRF_TOKEN, token);
+       printf("<input type=\"hidden\" name=\"%s\" value=\"%lld\">\n",
+              XSRF_TIME, (long long int)xsrf_time);
+}
+
+bool verify_xsrf_token(const char *formname)
+{
+       char expected[33];
+       const char *username = cgi_user_name();
+       const char *pass = cgi_user_pass();
+       const char *token = cgi_variable_nonull(XSRF_TOKEN);
+       const char *time_str = cgi_variable_nonull(XSRF_TIME);
+       char *p = NULL;
+       long long xsrf_time_ll = 0;
+       time_t xsrf_time = 0;
+       time_t now = time(NULL);
+
+       errno = 0;
+       xsrf_time_ll = strtoll(time_str, &p, 10);
+       if (errno != 0) {
+               return false;
+       }
+       if (p == NULL) {
+               return false;
+       }
+       if (PTR_DIFF(p, time_str) > strlen(time_str)) {
+               return false;
+       }
+       if (xsrf_time_ll > _TYPE_MAXIMUM(time_t)) {
+               return false;
+       }
+       if (xsrf_time_ll < _TYPE_MINIMUM(time_t)) {
+               return false;
+       }
+       xsrf_time = xsrf_time_ll;
+
+       if (abs(now - xsrf_time) > XSRF_TIMEOUT) {
+               return false;
+       }
+
+       get_xsrf_token(username, pass, formname, xsrf_time, expected);
+       return (strncmp(expected, token, sizeof(expected)) == 0);
+}
+
+
 /****************************************************************************
   include a lump of html in a page 
 ****************************************************************************/
@@ -255,16 +342,16 @@ static void show_parameter(int snum, struct parm_struct *parm)
                        for (;*list;list++) {
                                /* enclose in HTML encoded quotes if the string contains a space */
                                if ( strchr_m(*list, ' ') ) {
-                                       push_utf8_allocate(&utf8_s1, *list, &converted_size);
-                                       push_utf8_allocate(&utf8_s2, ((*(list+1))?", ":""), &converted_size);
+                                       push_utf8_talloc(talloc_tos(), &utf8_s1, *list, &converted_size);
+                                       push_utf8_talloc(talloc_tos(), &utf8_s2, ((*(list+1))?", ":""), &converted_size);
                                        printf("&quot;%s&quot;%s", utf8_s1, utf8_s2);
                                } else {
-                                       push_utf8_allocate(&utf8_s1, *list, &converted_size);
-                                       push_utf8_allocate(&utf8_s2, ((*(list+1))?", ":""), &converted_size);
+                                       push_utf8_talloc(talloc_tos(), &utf8_s1, *list, &converted_size);
+                                       push_utf8_talloc(talloc_tos(), &utf8_s2, ((*(list+1))?", ":""), &converted_size);
                                        printf("%s%s", utf8_s1, utf8_s2);
                                }
-                               SAFE_FREE(utf8_s1);
-                               SAFE_FREE(utf8_s2);
+                               TALLOC_FREE(utf8_s1);
+                               TALLOC_FREE(utf8_s2);
                        }
                }
                printf("\">");
@@ -285,10 +372,10 @@ static void show_parameter(int snum, struct parm_struct *parm)
 
        case P_STRING:
        case P_USTRING:
-               push_utf8_allocate(&utf8_s1, *(char **)ptr, &converted_size);
+               push_utf8_talloc(talloc_tos(), &utf8_s1, *(char **)ptr, &converted_size);
                printf("<input type=text size=40 name=\"parm_%s\" value=\"%s\">",
                       make_parm_name(parm->label), fix_quotes(ctx, utf8_s1));
-               SAFE_FREE(utf8_s1);
+               TALLOC_FREE(utf8_s1);
                printf("<input type=button value=\"%s\" onClick=\"swatform.parm_%s.value=\'%s\'\">",
                        _("Set Default"), make_parm_name(parm->label),fix_backslash((char *)(parm->def.svalue)));
                break;
@@ -490,7 +577,7 @@ static int save_reload(int snum)
                 return 0;
         }
        iNumNonAutoPrintServices = lp_numservices();
-       load_printers();
+       pcap_cache_reload(&load_printers);
 
        return 1;
 }
@@ -611,13 +698,20 @@ static void welcome_page(void)
 static void viewconfig_page(void)
 {
        int full_view=0;
+       const char form_name[] = "viewconfig";
+
+       if (!verify_xsrf_token(form_name)) {
+               goto output_page;
+       }
 
        if (cgi_variable("full_view")) {
                full_view = 1;
        }
 
+output_page:
        printf("<H2>%s</H2>\n", _("Current Config"));
        printf("<form method=post>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), form_name);
 
        if (full_view) {
                printf("<input type=submit name=\"normal_view\" value=\"%s\">\n", _("Normal View"));
@@ -637,18 +731,25 @@ static void viewconfig_page(void)
 static void wizard_params_page(void)
 {
        unsigned int parm_filter = FLAG_WIZARD;
+       const char form_name[] = "wizard_params";
 
        /* Here we first set and commit all the parameters that were selected
           in the previous screen. */
 
        printf("<H2>%s</H2>\n", _("Wizard Parameter Edit Page"));
 
+       if (!verify_xsrf_token(form_name)) {
+               goto output_page;
+       }
+
        if (cgi_variable("Commit")) {
                commit_parameters(GLOBAL_SECTION_SNUM);
-               save_reload(0);
+               save_reload(-1);
        }
 
+output_page:
        printf("<form name=\"swatform\" method=post action=wizard_params>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), form_name);
 
        if (have_write_access) {
                printf("<input type=submit name=\"Commit\" value=\"Commit Changes\">\n");
@@ -669,7 +770,7 @@ static void wizard_params_page(void)
 static void rewritecfg_file(void)
 {
        commit_parameters(GLOBAL_SECTION_SNUM);
-       save_reload(0);
+       save_reload(-1);
        printf("<H2>%s</H2>\n", _("Note: smb.conf file has been read and rewritten"));
 }
 
@@ -684,6 +785,11 @@ static void wizard_page(void)
        int have_home = -1;
        int HomeExpo = 0;
        int SerType = 0;
+       const char form_name[] = "wizard";
+
+       if (!verify_xsrf_token(form_name)) {
+               goto output_page;
+       }
 
        if (cgi_variable("Rewrite")) {
                (void) rewritecfg_file();
@@ -757,7 +863,7 @@ static void wizard_page(void)
                }
 
                commit_parameters(GLOBAL_SECTION_SNUM);
-               save_reload(0);
+               save_reload(-1);
        }
        else
        {
@@ -774,10 +880,12 @@ static void wizard_page(void)
                winstype = 3;
 
        role = lp_server_role();
-       
+
+output_page:
        /* Here we go ... */
        printf("<H2>%s</H2>\n", _("Samba Configuration Wizard"));
        printf("<form method=post action=wizard>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), form_name);
 
        if (have_write_access) {
                printf("%s\n", _("The \"Rewrite smb.conf file\" button will clear the smb.conf file of all default values and of comments."));
@@ -846,12 +954,17 @@ static void globals_page(void)
 {
        unsigned int parm_filter = FLAG_BASIC;
        int mode = 0;
+       const char form_name[] = "globals";
 
        printf("<H2>%s</H2>\n", _("Global Parameters"));
 
+       if (!verify_xsrf_token(form_name)) {
+               goto output_page;
+       }
+
        if (cgi_variable("Commit")) {
                commit_parameters(GLOBAL_SECTION_SNUM);
-               save_reload(0);
+               save_reload(-1);
        }
 
        if ( cgi_variable("ViewMode") )
@@ -861,7 +974,9 @@ static void globals_page(void)
        if ( cgi_variable("AdvMode"))
                mode = 1;
 
+output_page:
        printf("<form name=\"swatform\" method=post action=globals>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), form_name);
 
        ViewModeBoxes( mode );
        switch ( mode ) {
@@ -901,21 +1016,27 @@ static void shares_page(void)
        int mode = 0;
        unsigned int parm_filter = FLAG_BASIC;
        size_t converted_size;
+       const char form_name[] = "shares";
+
+       printf("<H2>%s</H2>\n", _("Share Parameters"));
+
+       if (!verify_xsrf_token(form_name)) {
+               goto output_page;
+       }
 
        if (share)
                snum = lp_servicenumber(share);
 
-       printf("<H2>%s</H2>\n", _("Share Parameters"));
 
        if (cgi_variable("Commit") && snum >= 0) {
                commit_parameters(snum);
-               save_reload(0);
+               save_reload(-1);
                snum = lp_servicenumber(share);
        }
 
        if (cgi_variable("Delete") && snum >= 0) {
                lp_remove_service(snum);
-               save_reload(0);
+               save_reload(-1);
                share = NULL;
                snum = -1;
        }
@@ -931,10 +1052,6 @@ static void shares_page(void)
                }
        }
 
-       printf("<FORM name=\"swatform\" method=post>\n");
-
-       printf("<table>\n");
-
        if ( cgi_variable("ViewMode") )
                mode = atoi(cgi_variable_nonull("ViewMode"));
        if ( cgi_variable("BasicMode"))
@@ -942,6 +1059,12 @@ static void shares_page(void)
        if ( cgi_variable("AdvMode"))
                mode = 1;
 
+output_page:
+       printf("<FORM name=\"swatform\" method=post>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), form_name);
+
+       printf("<table>\n");
+
        ViewModeBoxes( mode );
        switch ( mode ) {
                case 0:
@@ -959,11 +1082,11 @@ static void shares_page(void)
        for (i=0;i<lp_numservices();i++) {
                s = lp_servicename(i);
                if (s && (*s) && strcmp(s,"IPC$") && !lp_print_ok(i)) {
-                       push_utf8_allocate(&utf8_s, s, &converted_size);
+                       push_utf8_talloc(talloc_tos(), &utf8_s, s, &converted_size);
                        printf("<option %s value=\"%s\">%s\n", 
                               (share && strcmp(share,s)==0)?"SELECTED":"",
                               utf8_s, utf8_s);
-                       SAFE_FREE(utf8_s);
+                       TALLOC_FREE(utf8_s);
                }
        }
        printf("</select></td>\n");
@@ -1121,11 +1244,9 @@ static void chg_passwd(void)
        if(cgi_variable(CHG_S_PASSWD_FLAG)) {
                printf("<p>");
                if (rslt == True) {
-                       printf(_(" The passwd for '%s' has been changed."), cgi_variable_nonull(SWAT_USER));
-                       printf("\n");
+                       printf("%s\n", _(" The passwd has been changed."));
                } else {
-                       printf(_(" The passwd for '%s' has NOT been changed."), cgi_variable_nonull(SWAT_USER));
-                       printf("\n");
+                       printf("%s\n", _(" The passwd has NOT been changed."));
                }
        }
        
@@ -1138,20 +1259,15 @@ static void chg_passwd(void)
 static void passwd_page(void)
 {
        const char *new_name = cgi_user_name();
-
-       /* 
-        * After the first time through here be nice. If the user
-        * changed the User box text to another users name, remember it.
-        */
-       if (cgi_variable(SWAT_USER)) {
-               new_name = cgi_variable_nonull(SWAT_USER);
-       } 
+       const char passwd_form[] = "passwd";
+       const char rpasswd_form[] = "rpasswd";
 
        if (!new_name) new_name = "";
 
        printf("<H2>%s</H2>\n", _("Server Password Management"));
 
        printf("<FORM name=\"swatform\" method=post>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), passwd_form);
 
        printf("<table>\n");
 
@@ -1191,14 +1307,16 @@ static void passwd_page(void)
         * Do some work if change, add, disable or enable was
         * requested. It could be this is the first time through this
         * code, so there isn't anything to do.  */
-       if ((cgi_variable(CHG_S_PASSWD_FLAG)) || (cgi_variable(ADD_USER_FLAG)) || (cgi_variable(DELETE_USER_FLAG)) ||
-           (cgi_variable(DISABLE_USER_FLAG)) || (cgi_variable(ENABLE_USER_FLAG))) {
+       if (verify_xsrf_token(passwd_form) &&
+          ((cgi_variable(CHG_S_PASSWD_FLAG)) || (cgi_variable(ADD_USER_FLAG)) || (cgi_variable(DELETE_USER_FLAG)) ||
+           (cgi_variable(DISABLE_USER_FLAG)) || (cgi_variable(ENABLE_USER_FLAG)))) {
                chg_passwd();           
        }
 
        printf("<H2>%s</H2>\n", _("Client/Server Password Management"));
 
        printf("<FORM name=\"swatform\" method=post>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), rpasswd_form);
 
        printf("<table>\n");
 
@@ -1231,7 +1349,7 @@ static void passwd_page(void)
         * password somewhere other than the server. It could be this
         * is the first time through this code, so there isn't
         * anything to do.  */
-       if (cgi_variable(CHG_R_PASSWD_FLAG)) {
+       if (verify_xsrf_token(passwd_form) && cgi_variable(CHG_R_PASSWD_FLAG)) {
                chg_passwd();           
        }
 
@@ -1248,30 +1366,27 @@ static void printers_page(void)
        int i;
        int mode = 0;
        unsigned int parm_filter = FLAG_BASIC;
+       const char form_name[] = "printers";
+
+       if (!verify_xsrf_token(form_name)) {
+               goto output_page;
+       }
 
        if (share)
                snum = lp_servicenumber(share);
 
-        printf("<H2>%s</H2>\n", _("Printer Parameters"));
-        printf("<H3>%s</H3>\n", _("Important Note:"));
-        printf("%s",_("Printer names marked with [*] in the Choose Printer drop-down box "));
-        printf("%s",_("are autoloaded printers from "));
-        printf("<A HREF=\"/swat/help/smb.conf.5.html#printcapname\" target=\"docs\">%s</A>\n", _("Printcap Name"));
-        printf("%s\n", _("Attempting to delete these printers from SWAT will have no effect."));
-
        if (cgi_variable("Commit") && snum >= 0) {
                commit_parameters(snum);
                if (snum >= iNumNonAutoPrintServices)
                    save_reload(snum);
                else
-                   save_reload(0);
+                   save_reload(-1);
                snum = lp_servicenumber(share);
        }
 
        if (cgi_variable("Delete") && snum >= 0) {
                lp_remove_service(snum);
-               save_reload(0);
+               save_reload(-1);
                share = NULL;
                snum = -1;
        }
@@ -1288,8 +1403,6 @@ static void printers_page(void)
                }
        }
 
-       printf("<FORM name=\"swatform\" method=post>\n");
-
        if ( cgi_variable("ViewMode") )
                mode = atoi(cgi_variable_nonull("ViewMode"));
         if ( cgi_variable("BasicMode"))
@@ -1297,6 +1410,19 @@ static void printers_page(void)
         if ( cgi_variable("AdvMode"))
                 mode = 1;
 
+output_page:
+        printf("<H2>%s</H2>\n", _("Printer Parameters"));
+
+        printf("<H3>%s</H3>\n", _("Important Note:"));
+        printf("%s",_("Printer names marked with [*] in the Choose Printer drop-down box "));
+        printf("%s",_("are autoloaded printers from "));
+        printf("<A HREF=\"/swat/help/smb.conf.5.html#printcapname\" target=\"docs\">%s</A>\n", _("Printcap Name"));
+        printf("%s\n", _("Attempting to delete these printers from SWAT will have no effect."));
+
+
+       printf("<FORM name=\"swatform\" method=post>\n");
+       print_xsrf_token(cgi_user_name(), cgi_user_pass(), form_name);
+
        ViewModeBoxes( mode );
        switch ( mode ) {
                case 0:
@@ -1434,7 +1560,7 @@ const char *lang_msg_rotate(TALLOC_CTX *ctx, const char *msgid)
        load_config(True);
        load_interfaces();
        iNumNonAutoPrintServices = lp_numservices();
-       load_printers();
+       pcap_cache_reload(&load_printers);
 
        cgi_setup(get_dyn_SWATDIR(), !demo_mode);