Check-in [91893d0dc3]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Started work on running Fossil as different OS user per Flint user
Timelines: family | ancestors | descendants | both | setuid-fossil
Files: files | file ages | folders
SHA1:91893d0dc39ba9f1efa44037f0685572c8d5b443
User & Date: rkeene 2019-01-07 22:40:17
Context
2019-01-07
23:07
Ensure User ID is sane before calling fossil check-in: 0874801b3e user: rkeene tags: setuid-fossil
22:40
Started work on running Fossil as different OS user per Flint user check-in: 91893d0dc3 user: rkeene tags: setuid-fossil
2017-03-16
16:32
Fixed missing semicolons Leaf check-in: f6600ad1a1 user: rkeene tags: trunk
Changes

Added .fossil-settings/ignore-glob.

            1  +scripts/fossil-as-user/suid-fossil
            2  +scripts/fossil-as-user/lib/*

Added scripts/fossil-as-user/Makefile.

            1  +CRYSTAL_PATH := /opt/appfs/rkeene.org/crystal/platform/latest
            2  +CRYSTAL      := $(CRYSTAL_PATH)/bin/crystal
            3  +SHARDS       := $(CRYSTAL_PATH)/bin/shards
            4  +DEBUG_OR_RELEASE := --debug
            5  +
            6  +all: suid-fossil
            7  +
            8  +lib: shard.lock
            9  +	rm -rf lib
           10  +	$(SHARDS) install
           11  +
           12  +shard.lock: shard.yml
           13  +
           14  +suid-fossil: suid-fossil.cr lib
           15  +	$(CRYSTAL) build $(DEBUG_OR_RELEASE) -o suid-fossil suid-fossil.cr
           16  +
           17  +clean:
           18  +	rm -f suid-fossil
           19  +
           20  +distclean: clean
           21  +	rm -rf lib
           22  +
           23  +mrproper: distclean
           24  +	rm -f shard.lock
           25  +	$(MAKE) lib
           26  +	rm -rf lib
           27  +
           28  +.PHONY: all clean distclean
           29  +.SUFFIXES:

Added scripts/fossil-as-user/shard.lock.

            1  +version: 1.0
            2  +shards:
            3  +  db:
            4  +    github: crystal-lang/crystal-db
            5  +    version: 0.5.1
            6  +
            7  +  sqlite3:
            8  +    github: crystal-lang/crystal-sqlite3
            9  +    version: 0.10.0
           10  +

Added scripts/fossil-as-user/shard.yml.

            1  +name: fossil-as-user
            2  +version: 1
            3  +dependencies:
            4  +  sqlite3:
            5  +    github: crystal-lang/crystal-sqlite3

Added scripts/fossil-as-user/suid-fossil.cr.

            1  +#! /opt/appfs/rkeene.org/crystal/platform/latest/bin/crystal
            2  +
            3  +require "sqlite3"
            4  +require "ini"
            5  +
            6  +# User-specified constants
            7  +## UID offset when mapping Flint user IDs to OS user IDs
            8  +UID_OFFSET = 1024 * 1024
            9  +
           10  +## 
           11  +FLINT_ROOT = "/home/chiselapp/chisel"
           12  +
           13  +# Import the C functions for setuid/setgid
           14  +lib C
           15  +	fun setuid(uid : Int32) : Int32
           16  +	fun setgid(gid : Int32) : Int32
           17  +end
           18  +
           19  +# Usage information
           20  +def print_help
           21  +	print("Usage: suid-fossil <fossil-args...>\n")
           22  +end
           23  +
           24  +# Convert a UserID or a Username to the other via the DB
           25  +def userid_from_db(userdb : String, username : String) : Int32
           26  +	userid = nil
           27  +
           28  +	DB.open "sqlite3://#{userdb}" {|db|
           29  +		userid = db.query_one "SELECT id FROM users WHERE username = ? LIMIT 1", username, as: {Int32}
           30  +	}
           31  +
           32  +	if userid.nil?
           33  +		raise "User Name could not be found"
           34  +	end
           35  +
           36  +	userid
           37  +end
           38  +
           39  +def username_from_db(userdb : String, userid : Int32) : String
           40  +	username = nil
           41  +
           42  +	DB.open "sqlite3://#{userdb}" {|db|
           43  +		username = db.query_one "SELECT username FROM users WHERE id = ? LIMIT 1", userid, as: {String}
           44  +	}
           45  +
           46  +	if username.nil?
           47  +		raise "User ID could not be found"
           48  +	end
           49  +
           50  +	username
           51  +end
           52  +
           53  +# Find the Flint DB given a path to the Flint root
           54  +def find_db(root : String) : String
           55  +	dbconfig_file = File.join(root, "config", "sqlite.cnf")
           56  +
           57  +	dbconfig_text = File.read(dbconfig_file)
           58  +
           59  +	dbconfig = INI.parse(dbconfig_text)
           60  +
           61  +	dbfile = dbconfig["config"]["database"]
           62  +	dbfile = File.expand_path(dbfile, File.join(root, "db"))
           63  +
           64  +	dbfile
           65  +end
           66  +
           67  +# Find a Flint User ID from an OS File
           68  +def userid_from_file(file : String) : Int32
           69  +	info = File.info(file)
           70  +
           71  +	Int32.new(info.owner - UID_OFFSET)
           72  +end
           73  +
           74  +# Run Fossil, wrapped as a Flint UserName/UserID
           75  +def suid_fossil(username : String, userid : Int32, fossil_args : Array, fossil_command = "fossil")
           76  +	# Compute OS UID from Flint User ID
           77  +	uid = userid + UID_OFFSET
           78  +
           79  +	# Create Fossil home directory
           80  +	home = "/tmp/suid-fossil/#{userid}"
           81  +
           82  +	if !Dir.exists?(home)
           83  +		Dir.mkdir(home, 0o700)
           84  +		File.chown(home, uid, uid)
           85  +	end
           86  +
           87  +	ENV["HOME"] = home
           88  +
           89  +	# Set OS UID/GID
           90  +	## Opportunistic -- if it fails, we do not care
           91  +	C.setgid(uid)
           92  +
           93  +	uidcheck = C.setuid(uid)
           94  +	if (uidcheck != 0)
           95  +		raise "Unable to switch to UID #{uid}"
           96  +	end
           97  +
           98  +	# If possible, update environment with usernames
           99  +	ENV["USER"] = username
          100  +	ENV["USERNAME"] = username
          101  +
          102  +	Process.exec(fossil_command, fossil_args)
          103  +
          104  +	raise "Failed to run Fossil"
          105  +end
          106  +
          107  +# -------------------------------
          108  +# MAIN
          109  +# -------------------------------
          110  +fossil_args = ARGV
          111  +
          112  +flint_root = ENV.fetch("FLINT_ROOT", FLINT_ROOT)
          113  +userid     = ENV["FLINT_USERID"]?
          114  +username   = ENV["FLINT_USERNAME"]?
          115  +
          116  +# Find DB if possible
          117  +userdb   = ENV["FLINT_USERDB"]?
          118  +
          119  +if userdb.nil?
          120  +	if !flint_root.nil?
          121  +		userdb = find_db(flint_root)
          122  +	end
          123  +end
          124  +
          125  +# Find User ID
          126  +## Check to see if this is a CGI call, if so
          127  +## we take the user ID from the filename
          128  +if userid.nil?
          129  +	if ENV.has_key?("GATEWAY_INTERFACE")
          130  +		userid = userid_from_file(ARGV[0])
          131  +	end
          132  +end
          133  +
          134  +if userid.nil?
          135  +	if username.nil?
          136  +		raise "Unhandled -- must specify one of FLINT_USERNAME or FLINT_USERID"
          137  +	end
          138  +
          139  +	if userdb.nil?
          140  +		raise "Unhandled -- must specify FLINT_USERDB or FLINT_ROOT"
          141  +	end
          142  +
          143  +	userid = userid_from_db(userdb, username)
          144  +else
          145  +	userid = userid.to_i32()
          146  +end
          147  +
          148  +# Find User Name
          149  +if username.nil?
          150  +	if userdb.nil?
          151  +		raise "Unhandled -- must specify FLINT_USERDB or FLINT_ROOT"
          152  +	end
          153  +
          154  +	username = username_from_db(userdb, userid)
          155  +end
          156  +
          157  +# Run Fossil
          158  +suid_fossil(username, userid, fossil_args)