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)