I expanded upon this some more.

-fixed some security concerns; fossil ui no longer is a bypass
-fixed an issue with access() and setuid logic
-the web interface has a place to change the posix id
-no need to manually modify tables
-changes contained within #ifdefs (exepct on the scheme.c part were I couldn't 
figure out how)

I can't get this into production fast enough! Maybe someone else feels the need 
to.

-------------

Experiment Howto

project name: alpha
administrator account alpha-adm
project specific fossil binary: /usr/bin/fossil-alpha
project user: alpha-user-a


Setup user alpha-adm:

# useradd -s /bin/bash alpha-adm -m
...


Setup user alpha-user-a:

# useradd -s /bin/bash -m alpha-user-a
...
(make sure these home dir perms are setup the right way - ie not ubuntu 
defaults)


Setup project specfic fossil binary:

# cp fossil /usr/local/bin/fossil-alpha
# chown alpha-adm:alpha-adm /usr/local/bin/fossil-alpha
# chmod u+s /usr/local/bin/fossil-alpha

Initialize the project:

alpha-adm@host:~$ fossil-alpha init ./alpha.fossil
alpha-adm@host:~$ fossil-alpha ui ./alpha.fossil

-remove permissions for nobody for demonstration

Demonstrate regular fossil binary cannot access the project:

alpha-user-a@host:~$ fossil clone /home/alpha-adm/alpha.fossil 
alpha-clone.fossil
fossil: cannot open "/home/alpha-adm/alpha.fossil" for reading

Demonstrate project specific fossil binary can not locally access the project 
for non-admins:

alpha-user-a@host:~$ fossil-alpha clone /home/alpha-adm/alpha.fossil 
alpha-clone.fossil
fossil-alpha: cannot open "alpha-clone.fossil" for writing
(misleading error. you follow?)
I think this is still a security hole. I need to find the code path for local 
file based urls. 

Add user to fossil:

alpha-adm@host:~$ fossil-alpha ui ./alpha.fossil

-add a user alpha-user-a and fill in the posix uid field
-grant just clone rights for demo

Regular fossil binary over ssh still yields no access:

$ fossil clone 
ssh://alpha-user-a:*@127.0.0.1:29//home/alpha-adm/alpha.fossil?fossil=/usr/local/bin/fossil
 alpha-clone.fossil
ssh -e none -T -p 29 [email protected]
                Bytes      Cards  Artifacts     Deltas
Sent:              53          1          0          0
Received:         218          1          0          0
Sent:              58          1          0          0
Error: Database error: attempt to write a readonly database
UPDATE event SET mtime=(SELECT m1 FROM time_fudge WHERE mid=objid) WHERE objid 
IN (SELECT mid FROM time_fudge);DROP TABLE time_fudge;
Received:         218          1          0          0
Total network traffic: 518 bytes sent, 794 bytes received
fossil: server returned an error - clone aborted

Errors are misleading. user alpha-user-a has now way to read the file 
/home/alpha-adm/alpha.fossil

Project specific fossil with no remote user yields no access:

$ fossil clone 
ssh://alpha-user-a:*@127.0.0.1:29//home/alpha-adm/alpha.fossil?fossil=/usr/local/bin/fossil-alpha
 alpha-clone.fossil
ssh -e none -T -p 29 [email protected]
                Bytes      Cards  Artifacts     Deltas
Sent:              53          1          0          0
Received:         152          3          0          0
Sent:              68          2          0          0
Error: not authorized to clone
Received:         152          2          0          0
Total network traffic: 529 bytes sent, 678 bytes received
fossil: server returned an error - clone aborted

If this worked without error, then you probably forget to remove clone rights 
for nobody.

Successful run:

$ REMOTE_USER=alpha-user-a fossil clone 
ssh://alpha-user-a:*@127.0.0.1:29//home/alpha-adm/alpha.fossil?fossil=/usr/local/bin/fossil-alpha
 alpha-clone.fossil
ssh -e none -T -p 29 [email protected]
                Bytes      Cards  Artifacts     Deltas
Sent:              53          1          0          0
Received:         664          9          3          0
Sent:              58          2          0          0
Received:         634          2          0          0
Total network traffic: 592 bytes sent, 1442 bytes received
Closing SSH tunnel: Rebuilding repository meta-data...
  100.0% complete...
project-id: ad7b7fb1627b88bf2719b8ad674fd54940f3e27b
server-id:  9499b9e1f4b95e549a658206902b57534f91c9b3
admin-user: alpha-user-a (password is "db1ac0")


No show that access is not all or nothing. user alpha-user-a has only has clone 
permission, not push:

alpha-user-a@host:~$ mkdir checkout
alpha-user-a@host:~$ cd checkout/
alpha-user-a@host:~/checkout$ fossil open ../alpha-clone.fossil
...
alpha-user-a@host:~/checkout$ echo "good stuff" > goodfile
alpha-user-a@host:~/checkout$ fossil add goodfile
...
alpha-user-a@host:~/checkout$ REMOTE_USER=alpha-user-a fossil commit
Autosync:  
ssh://alpha-user-a:*@127.0.0.1:29//home/alpha-adm/alpha.fossil?fossil=/usr/local/bin/fossil-alpha
ssh -e none -T -p 29 [email protected]
...
continue in spite of sync failure (y/N)? y
...
# you are done:
comment
...
fossil: Autosync failed

Now we grant alpha-user-a push authority:

alpha-adm@host:~$ fossil-alpha ui ./alpha.fossil

and try again:

alpha-user-a@host:~/checkout$ REMOTE_USER=alpha-user-a fossil push
Server:    
ssh://alpha-user-a:*@127.0.0.1:29//home/alpha-adm/alpha.fossil?fossil=/usr/local/bin/fossil-alpha
ssh -e none -T -p 29 [email protected]
                Bytes      Cards  Artifacts     Deltas
Sent:             360          6          0          0
Received:         126          3          0          0
Sent:             716          6          1          1
Received:          32          1          0          0
Total network traffic: 1127 bytes sent, 557 bytes received


-cts 10/10/12


7 diffs to play:
============
$ diff -u fossil-src-20120808112557/src/http_transport.c 
fossil-src-modified/src/http_transport.c
--- fossil-src-20120808112557/src/http_transport.c      2012-08-08 
06:49:21.000000000 -0500
+++ fossil-src-modified/src/http_transport.c    2012-10-10 15:41:37.736464075 
-0500
@@ -80,7 +80,7 @@
 ** Read text from sshIn.  Zero-terminate and remove trailing
 ** whitespace.
 */
-static void sshin_read(char *zBuf, int szBuf){
+static int sshin_read(char *zBuf, int szBuf){
   int got;
   zBuf[0] = 0;
   got = read(sshIn, zBuf, szBuf-1);
@@ -89,6 +89,8 @@
     if( got==0 || !fossil_isspace(zBuf[got-1]) ) break;
     got--;
   }
+
+ return got;
 }

 /*
@@ -112,6 +114,7 @@
     Blob zCmd;         /* The SSH command */
     char *zHost;       /* The host name to contact */
     char *zIn;         /* An input line received back from remote */
+    const char *zRemoteUser = P("REMOTE_USER"); /* environment variable */

     zSsh = db_get("ssh-command", zDefaultSshCmd);
     blob_init(&zCmd, zSsh, -1);
@@ -157,16 +160,36 @@
     }
     blob_reset(&zCmd);

+#ifdef FOSSIL_SETUID_SUPPORT
+    /* Attempt to send our local environment variable over to the server if
+    ** it is present.
+    */
+    if(zRemoteUser) {
+     fprintf(sshOut, "export REMOTE_USER=%s\n", zRemoteUser);
+     fflush(sshOut);
+    }
+#endif
+
     /* Send an "echo" command to the other side to make sure that the
     ** connection is up and working.
     */
     fprintf(sshOut, "echo test\n");
     fflush(sshOut);
     zIn = fossil_malloc(16000);
-    sshin_read(zIn, 16000);
-    if( memcmp(zIn, "test", 4)!=0 ){
+
+    int read_length, failed = 1;
+    while((read_length = sshin_read(zIn, 16000)) > 0)
+    {
+     if((memcmp(zIn + read_length - 4, "test", 4)) == 0)
+     {
+      failed = 0;
+      break;
+     }
+   }
+
+   if( failed) {
       pclose2(sshIn, sshOut, sshPid);
-      fossil_fatal("ssh connection failed: [%s]", zIn);
+      fossil_fatal("ssh connection failed loopback echo test: [%s]", zIn);
     }
     fossil_free(zIn);
   }
============
$ diff -u fossil-src-20120808112557/src/http.c fossil-src-modified/src/http.c  
--- fossil-src-20120808112557/src/http.c        2012-08-08 06:49:21.000000000 
-0500
+++ fossil-src-modified/src/http.c      2012-10-10 15:45:03.278967955 -0500
@@ -39,12 +39,20 @@
   const char *zPw;     /* The user password */
   Blob pw;             /* The nonce with user password appended */
   Blob sig;            /* The signature field */
+#ifdef FOSSIL_SETUID_SUPPORT
+  const char *zRemoteUser = P("REMOTE_USER");
+#endif

   blob_zero(pLogin);
   if( g.urlUser==0 || fossil_strcmp(g.urlUser, "anonymous")==0 ){
      return;  /* If no login card for users "nobody" and "anonymous" */
   }
-  if( g.urlIsSsh ){
+
+  if( g.urlIsSsh
+#ifdef FOSSIL_SETUID_SUPPORT
+      && (zRemoteUser == NULL)
+#endif
+    ) {
      return;  /* If no login card for SSH: */
   }
   blob_zero(&nonce);
============
$ diff -u fossil-src-20120808112557/src/login.c fossil-src-modified/src/login.c
--- fossil-src-20120808112557/src/login.c       2012-08-08 06:49:21.000000000 
-0500
+++ fossil-src-modified/src/login.c     2012-10-10 16:41:42.266463243 -0500
@@ -776,6 +776,11 @@
   char *zRemoteAddr;            /* Abbreviated IP address of the requestor */
   const char *zCap = 0;         /* Capability string */
   const char *zPublicPages = 0; /* GLOB patterns of public pages */
+  const char *zRemoteUser = P("REMOTE_USER");
+#ifdef FOSSIL_SETUID_SUPPORT
+  uid_t realUserid = getuid();
+  uid_t effectiveUserid = geteuid();
+#endif

   /* Only run this check once.  */
   if( g.userUid!=0 ) return;
@@ -795,12 +800,23 @@
    && g.useLocalauth
    && db_get_int("localauth",0)==0
    && P("HTTPS")==0
+#ifdef FOSSIL_SETUID_SUPPORT
+   && zRemoteUser == NULL
+#endif
   ){
-    uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
-    g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
-    zCap = "sx";
-    g.noPswd = 1;
-    sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
+#ifdef FOSSIL_SETUID_SUPPORT
+    /* fossil ui in a setuid security model check */
+    if(realUserid == effectiveUserid)
+    {
+#endif
+     uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
+     g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
+     zCap = "sx";
+     g.noPswd = 1;
+     sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "localhost");
+#ifdef FOSSIL_SETUID_SUPPORT
+    }
+#endif
   }

   /* Check the login cookie to see if it matches a known valid user.
@@ -863,13 +879,30 @@
   ** then accept the value of REMOTE_USER as the user.
   */
   if( uid==0 ){
-    const char *zRemoteUser = P("REMOTE_USER");
     if( zRemoteUser && db_get_boolean("remote_user_ok",0) ){
       uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
                       " AND length(cap)>0 AND length(pw)>0", zRemoteUser);
     }
   }

+#ifdef FOSSIL_SETUID_SUPPORT
+  /* REMOTE_USER + POSIX security model is in use
+  **
+  */
+  if( uid==0 && zRemoteUser)
+  {
+      uid = db_int(0, "SELECT uid FROM user WHERE login=%Q"
+                      " AND length(cap)>0", zRemoteUser);
+
+    if( zRemoteUser && uid) {
+     if( (i64) (realUserid) != db_int64(-1, "SELECT posix_uid FROM user WHERE 
uid=%d", uid))
+     {
+      uid = 0;
+     }
+    }
+  }
+#endif
+
   /* If no user found yet, try to log in as "nobody" */
   if( uid==0 ){
     uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'");
============
$ diff -u fossil-src-20120808112557/src/file.c fossil-src-modified/src/file.c  
--- fossil-src-20120808112557/src/file.c        2012-08-08 06:49:21.000000000 
-0500
+++ fossil-src-modified/src/file.c      2012-10-10 15:18:43.938958841 -0500
@@ -29,6 +29,9 @@
 #include <string.h>
 #include <errno.h>
 #include "file.h"
+#ifdef FOSSIL_SETUID_SUPPORT
+#include <fcntl.h>
+#endif

 /*
 ** On Windows, include the Platform SDK header file.
@@ -300,9 +303,26 @@
 */
 int file_access(const char *zFilename, int flags){
   char *zMbcs = fossil_utf8_to_mbcs(zFilename);
+#ifdef FOSSIL_SETUID_SUPPORT
+  int fd, open_flags = 0;
+  if(flags & R_OK)
+   open_flags |= O_RDONLY;
+
+  if(flags & W_OK)
+   open_flags |= O_WRONLY;
+
+  fd = open(zMbcs, open_flags);
+  if(fd) {
+   close(fd);
+   return 0;
+  } else {
+   return -1;
+  }
+#else
   int rc = access(zMbcs, flags);
   fossil_mbcs_free(zMbcs);
   return rc;
+#endif
 }

 /*
============
$ diff -u fossil-src-20120808112557/src/setup.c fossil-src-modified/src/setup.c
--- fossil-src-20120808112557/src/setup.c       2012-08-08 06:49:21.000000000 
-0500
+++ fossil-src-modified/src/setup.c     2012-10-10 17:37:06.168957260 -0500
@@ -295,6 +295,9 @@
   int uid;
   int higherUser = 0;  /* True if user being edited is SETUP and the */
                        /* user doing the editing is ADMIN.  Disallow editing */
+#ifdef FOSSIL_SETUID_SUPPORT
+  int posix_uid = -1;
+#endif

   /* Must have ADMIN privleges to access this page
   */
@@ -321,7 +324,11 @@
   ** modified user record.  After writing the user record, redirect
   ** to the page that displays a list of users.
   */
-  doWrite = cgi_all("login","info","pw") && !higherUser;
+  doWrite = cgi_all("login","info","pw",
+#ifdef FOSSIL_SETUID_SUPPORT
+                    "posixuid"
+#endif
+                   ) && !higherUser;
   if( doWrite ){
     char zCap[50];
     int i = 0;
@@ -375,6 +382,9 @@
     zCap[i] = 0;
     zPw = P("pw");
     zLogin = P("login");
+#ifdef FOSSIL_SETUID_SUPPORT
+    sscanf(P("posixuid"), "%d", &posix_uid);
+#endif
     if( strlen(zLogin)==0 ){
       style_header("User Creation Error");
       @ <span class="loginError">Empty login not allowed.</span>
@@ -402,9 +412,15 @@
     }
     login_verify_csrf_secret();
     db_multi_exec(
+#ifdef FOSSIL_SETUID_SUPPORT
+       "REPLACE INTO user(uid,login,info,pw,cap,mtime,posix_uid) "
+       "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s',now(),%d)",
+      uid, P("login"), P("info"), zPw, zCap, posix_uid
+#else
        "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
        "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s',now())",
       uid, P("login"), P("info"), zPw, zCap
+#endif
     );
     if( atoi(PD("all","0"))>0 ){
       Blob sql;
@@ -425,8 +441,14 @@
         "  info=%Q,"
         "  cap=%Q,"
         "  mtime=now()"
+#ifdef FOSSIL_SETUID_SUPPORT
+        "  posix_uid=%d"
+#endif
         " WHERE login=%Q;",
-        zLogin, P("pw"), zLogin, P("info"), zCap,
+        zLogin, P("pw"), zLogin, P("info"), zCap,
+#ifdef FOSSIL_SETUID_SUPPORT
+        posix_uid,
+#endif
         zOldLogin
       );
       login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr);
@@ -457,6 +479,9 @@
     zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
     zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
     zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
+#ifdef FOSSIL_SETUID_SUPPORT
+    posix_uid = db_int(-1, "SELECT posix_uid FROM user WHERE uid=%d", uid);
+#endif
     if( strchr(zCap, 'a') ) oaa = " checked=\"checked\"";
     if( strchr(zCap, 'b') ) oab = " checked=\"checked\"";
     if( strchr(zCap, 'c') ) oac = " checked=\"checked\"";
@@ -545,6 +570,13 @@
   @   <td class="usetupEditLabel">Login:</td>
   @   <td><input type="text" name="login" value="%h(zLogin)" /></td>
   @ </tr>
+#ifdef FOSSIL_SETUID_SUPPORT
+  @ </tr>
+  @ <tr>
+  @   <td class="usetupEditLabel">POSIX UID:</td>
+  @   <td><input type="text" name="posixuid" value="%d(posix_uid)" /></td>
+  @ </tr>
+#endif
   @ <tr>
   @   <td class="usetupEditLabel">Contact Info:</td>
   @   <td><input type="text" name="info" size="40" value="%h(zInfo)" /></td>
============
$ diff -u fossil-src-20120808112557/src/schema.c 
fossil-src-modified/src/schema.c
--- fossil-src-20120808112557/src/schema.c      2012-08-08 06:49:21.000000000 
-0500
+++ fossil-src-modified/src/schema.c    2012-10-10 17:49:10.356484035 -0500
@@ -120,7 +120,8 @@
 @   cexpire DATETIME,               -- Time when cookie expires
 @   info TEXT,                      -- contact information
 @   mtime DATE,                     -- last change.  seconds since 1970
-@   photo BLOB                      -- JPEG image of this user
+@   photo BLOB,                     -- JPEG image of this user
+@   posix_uid INTEGER               -- used with setuid
 @ );
 @
 @ -- The VAR table holds miscellanous information about the repository.
============
$ diff -u fossil-src-20120808112557/src/rebuild.c 
fossil-src-modified/src/rebuild.c
--- fossil-src-20120808112557/src/rebuild.c     2012-08-08 06:49:21.000000000 
-0500
+++ fossil-src-modified/src/rebuild.c   2012-10-10 17:41:17.978979405 -0500
@@ -104,6 +104,9 @@
       "  info TEXT,\n"
       "  mtime DATE,\n"
       "  photo BLOB\n"
+#ifdef FOSSIL_SETUID_SUPPORT
+      "  posix_uid INTEGER\n"
+#endif
       ");"
       "INSERT OR IGNORE INTO user"
         " SELECT uid, login, pw, cap, cookie,"
============


www.thomasstover.com




_______________________________________________
fossil-users mailing list
[email protected]
http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users

Reply via email to