Goal: Fix Cross-Site Request Forgery vulnerability in SWAT

Fixes: Upstream security fix. CVE-2011-2522

Status wrt upstream: Fixed in 3.5.10

Author: Kai Blin <kai@samba.org>

Index: squeeze/source3/web/cgi.c
===================================================================
--- squeeze.orig/source3/web/cgi.c
+++ squeeze/source3/web/cgi.c
@@ -19,6 +19,8 @@
 
 #include "includes.h"
 #include "web/swat_proto.h"
+#include "secrets.h"
+#include "../lib/util/util.h"
 
 #define MAX_VARIABLES 10000
 
@@ -42,6 +44,7 @@
 static const char *baseurl;
 static char *pathinfo;
 static char *C_user;
+static char *C_pass;
 static bool inetd_server;
 static bool got_request;
 
@@ -320,7 +323,23 @@
 		exit(0);
 	}
 
-	setuid(0);
+	C_user = SMB_STRDUP(user);
+
+	if (!setuid(0)) {
+		C_pass = secrets_fetch_generic("root", "SWAT");
+		if (C_pass == NULL) {
+			char *tmp_pass = NULL;
+			tmp_pass = generate_random_str(talloc_tos(), 16);
+			if (tmp_pass == NULL) {
+				printf("%sFailed to create random nonce for "
+				       "SWAT session\n<br>%s\n", head, tail);
+				exit(0);
+			}
+			secrets_store_generic("root", "SWAT", tmp_pass);
+			C_pass = SMB_STRDUP(tmp_pass);
+			TALLOC_FREE(tmp_pass);
+		}
+	}
 	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", 
@@ -388,6 +407,7 @@
 			
 			/* Save the users name */
 			C_user = SMB_STRDUP(user);
+			C_pass = SMB_STRDUP(user_pass);
 			TALLOC_FREE(pass);
 			return True;
 		}
@@ -422,6 +442,13 @@
         return(C_user);
 }
 
+/***************************************************************************
+return a ptr to the users password
+  ***************************************************************************/
+char *cgi_user_pass(void)
+{
+        return(C_pass);
+}
 
 /***************************************************************************
 handle a file download
Index: squeeze/source3/web/statuspage.c
===================================================================
--- squeeze.orig/source3/web/statuspage.c
+++ squeeze/source3/web/statuspage.c
@@ -247,9 +247,14 @@
 	int nr_running=0;
 	bool waitup = False;
 	TALLOC_CTX *ctx = talloc_stackframe();
+	const char form_name[] = "status";
 
 	smbd_pid = pid_to_procid(pidfile_pid("smbd"));
 
+	if (!verify_xsrf_token(form_name)) {
+		goto output_page;
+	}
+
 	if (cgi_variable("smbd_restart") || cgi_variable("all_restart")) {
 		stop_smbd();
 		start_smbd();
@@ -326,9 +331,11 @@
 
 	initPid2Machine ();
 
+output_page:
 	printf("<H2>%s</H2>\n", _("Server Status"));
 
 	printf("<FORM method=post>\n");
+	print_xsrf_token(cgi_user_name(), cgi_user_pass(), form_name);
 
 	if (!autorefresh) {
 		printf("<input type=submit value=\"%s\" name=\"autorefresh\">\n", _("Auto Refresh"));
Index: squeeze/source3/web/swat.c
===================================================================
--- squeeze.orig/source3/web/swat.c
+++ squeeze/source3/web/swat.c
@@ -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 @@
 #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,76 @@
 	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]);
+		strncat(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);
+	time_t xsrf_time = 0;
+	time_t now = time(NULL);
+
+	if (sizeof(time_t) == sizeof(int)) {
+		xsrf_time = atoi(time_str);
+	} else if (sizeof(time_t) == sizeof(long)) {
+		xsrf_time = atol(time_str);
+	} else if (sizeof(time_t) == sizeof(long long)) {
+		xsrf_time = atoll(time_str);
+	}
+
+	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 
 ****************************************************************************/
@@ -611,13 +685,20 @@
 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 +718,25 @@
 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(-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");
@@ -684,6 +772,11 @@
 	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();
@@ -774,10 +867,12 @@
 		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,9 +941,14 @@
 {
 	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(-1);
@@ -861,7 +961,9 @@
 	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,11 +1003,17 @@
 	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);
@@ -931,10 +1039,6 @@
 		}
 	}
 
-	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 +1046,12 @@
 	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:
@@ -1136,6 +1246,8 @@
 static void passwd_page(void)
 {
 	const char *new_name = cgi_user_name();
+	const char passwd_form[] = "passwd";
+	const char rpasswd_form[] = "rpasswd";
 
 	if (!new_name) new_name = "";
 
Index: squeeze/source3/web/swat_proto.h
===================================================================
--- squeeze.orig/source3/web/swat_proto.h
+++ squeeze/source3/web/swat_proto.h
@@ -31,6 +31,7 @@
 const char *cgi_variable_nonull(const char *name);
 bool am_root(void);
 char *cgi_user_name(void);
+char *cgi_user_pass(void);
 void cgi_setup(const char *rootdir, int auth_required);
 const char *cgi_baseurl(void);
 const char *cgi_pathinfo(void);
@@ -66,5 +67,10 @@
 /* The following definitions come from web/swat.c  */
 
 const char *lang_msg_rotate(TALLOC_CTX *ctx, const char *msgid);
+void get_xsrf_token(const char *username, const char *pass,
+		    const char *formname, time_t xsrf_time, char token_str[33]);
+void print_xsrf_token(const char *username, const char *pass,
+		      const char *formname);
+bool verify_xsrf_token(const char *formname);
 
 #endif /*  _SWAT_PROTO_H_  */
