1-- SPDX-License-Identifier: BSD-2-Clause 2-- 3-- Copyright(c) 2022 Baptiste Daroussin <bapt@FreeBSD.org> 4 5local pu = require("posix.unistd") 6 7local function warnmsg(str) 8 io.stderr:write(str.."\n") 9end 10 11local function errmsg(str) 12 io.stderr:write(str.."\n") 13 os.exit(1) 14end 15 16local function dirname(oldpath) 17 if not oldpath then 18 return nil 19 end 20 local path = oldpath:gsub("[^/]+/*$", "") 21 if path == "" then 22 return nil 23 end 24 return path 25end 26 27local function mkdir_p(path) 28 if lfs.attributes(path, "mode") ~= nil then 29 return true 30 end 31 local r,err = mkdir_p(dirname(path)) 32 if not r then 33 return nil,err.." (creating "..path..")" 34 end 35 return lfs.mkdir(path) 36end 37 38local function sethostname(hostname) 39 if hostname == nil then return end 40 local root = os.getenv("NUAGE_FAKE_ROOTDIR") 41 if not root then 42 root = "" 43 end 44 local hostnamepath = root .. "/etc/rc.conf.d/hostname" 45 46 mkdir_p(dirname(hostnamepath)) 47 local f,err = io.open(hostnamepath, "w") 48 if not f then 49 warnmsg("Impossible to open "..hostnamepath .. ":" ..err) 50 return 51 end 52 f:write("hostname=\""..hostname.."\"\n") 53 f:close() 54end 55 56local function splitlist(list) 57 local ret = {} 58 if type(list) == "string" then 59 for str in list:gmatch("([^, ]+)") do 60 ret[#ret + 1] = str 61 end 62 elseif type(list) == "table" then 63 ret = list 64 else 65 warnmsg("Invalid type ".. type(list) ..", expecting table or string") 66 end 67 return ret 68end 69 70local function adduser(pwd) 71 if (type(pwd) ~= "table") then 72 warnmsg("Argument should be a table") 73 return nil 74 end 75 local root = os.getenv("NUAGE_FAKE_ROOTDIR") 76 local cmd = "pw " 77 if root then 78 cmd = cmd .. "-R " .. root .. " " 79 end 80 local f = io.popen(cmd .. " usershow " ..pwd.name .. " -7 2>/dev/null") 81 local pwdstr = f:read("*a") 82 f:close() 83 if pwdstr:len() ~= 0 then 84 return pwdstr:match("%a+:.+:%d+:%d+:.*:(.*):.*") 85 end 86 if not pwd.gecos then 87 pwd.gecos = pwd.name .. " User" 88 end 89 if not pwd.homedir then 90 pwd.homedir = "/home/" .. pwd.name 91 end 92 local extraargs="" 93 if pwd.groups then 94 local list = splitlist(pwd.groups) 95 extraargs = " -G ".. table.concat(list, ',') 96 end 97 -- pw will automatically create a group named after the username 98 -- do not add a -g option in this case 99 if pwd.primary_group and pwd.primary_group ~= pwd.name then 100 extraargs = extraargs .. " -g " .. pwd.primary_group 101 end 102 if not pwd.no_create_home then 103 extraargs = extraargs .. " -m " 104 end 105 if not pwd.shell then 106 pwd.shell = "/bin/sh" 107 end 108 local precmd = "" 109 local postcmd = "" 110 if pwd.passwd then 111 precmd = "echo "..pwd.passwd .. "| " 112 postcmd = " -H 0 " 113 elseif pwd.plain_text_passwd then 114 precmd = "echo "..pwd.plain_text_passwd .. "| " 115 postcmd = " -h 0 " 116 end 117 cmd = precmd .. "pw " 118 if root then 119 cmd = cmd .. "-R " .. root .. " " 120 end 121 cmd = cmd .. "useradd -n ".. pwd.name .. " -M 0755 -w none " 122 cmd = cmd .. extraargs .. " -c '".. pwd.gecos 123 cmd = cmd .. "' -d '" .. pwd.homedir .. "' -s "..pwd.shell .. postcmd 124 125 local r = os.execute(cmd) 126 if not r then 127 warnmsg("nuageinit: fail to add user "..pwd.name); 128 warnmsg(cmd) 129 return nil 130 end 131 if pwd.locked then 132 cmd = "pw " 133 if root then 134 cmd = cmd .. "-R " .. root .. " " 135 end 136 cmd = cmd .. "lock " .. pwd.name 137 os.execute(cmd) 138 end 139 return pwd.homedir 140end 141 142local function addgroup(grp) 143 if (type(grp) ~= "table") then 144 warnmsg("Argument should be a table") 145 return false 146 end 147 local root = os.getenv("NUAGE_FAKE_ROOTDIR") 148 local cmd = "pw " 149 if root then 150 cmd = cmd .. "-R " .. root .. " " 151 end 152 local f = io.popen(cmd .. " groupshow " ..grp.name .. " 2>/dev/null") 153 local grpstr = f:read("*a") 154 f:close() 155 if grpstr:len() ~= 0 then 156 return true 157 end 158 local extraargs = "" 159 if grp.members then 160 local list = splitlist(grp.members) 161 extraargs = " -M " .. table.concat(list, ',') 162 end 163 cmd = "pw " 164 if root then 165 cmd = cmd .. "-R " .. root .. " " 166 end 167 cmd = cmd .. "groupadd -n ".. grp.name .. extraargs 168 local r = os.execute(cmd) 169 if not r then 170 warnmsg("nuageinit: fail to add group ".. grp.name); 171 warnmsg(cmd) 172 return false 173 end 174 return true 175end 176 177local function addsshkey(homedir, key) 178 local chownak = false 179 local chowndotssh = false 180 local root = os.getenv("NUAGE_FAKE_ROOTDIR") 181 if root then 182 homedir = root .. "/" .. homedir 183 end 184 local ak_path = homedir .. "/.ssh/authorized_keys" 185 local dotssh_path = homedir .. "/.ssh" 186 local dirattrs = lfs.attributes(ak_path) 187 if dirattrs == nil then 188 chownak = true 189 dirattrs = lfs.attributes(dotssh_path) 190 if dirattrs == nil then 191 assert(lfs.mkdir(dotssh_path)) 192 chowndotssh = true 193 dirattrs = lfs.attributes(homedir) 194 end 195 end 196 197 local f = io.open(ak_path, "a") 198 if not f then 199 warnmsg("nuageinit: impossible to open "..ak_path) 200 return 201 end 202 f:write(key .. "\n") 203 f:close() 204 if chownak then 205 os.execute("chmod 0600 " .. ak_path) 206 pu.chown(ak_path, dirattrs.uid, dirattrs.gid) 207 end 208 if chowndotssh then 209 os.execute("chmod 0700 " .. dotssh_path) 210 pu.chown(dotssh_path, dirattrs.uid, dirattrs.gid) 211 end 212end 213 214local n = { 215 warn = warnmsg, 216 err = errmsg, 217 sethostname = sethostname, 218 adduser = adduser, 219 addgroup = addgroup, 220 addsshkey = addsshkey, 221 dirname = dirname, 222 mkdir_p = mkdir_p, 223} 224 225return n 226