user command to add and delete users

This commit is contained in:
Lisa Milne 2023-12-17 14:11:07 +10:00
parent b3965d720c
commit 98bedb8ca0
6 changed files with 528 additions and 195 deletions

View file

@ -2163,186 +2163,6 @@ Options:
return main(args);
});
clite.commands.load('su',function(args,env,io) {
var stdio = io.include('stdio');
var stdlib = io.include('stdlib');
var clite = io.include('clite');
var auth = io.include('auth');
var term = io.include('term');
var pw = null;
var preserve = true;
var shell = null;
function help() {
stdio.printf(`
su - switch users
Usage: su [OPTION]
Options:
-? Print this help information
-l Simulate a full login
-m Preserve the environment
-s <sh> Use the shell at <sh>
`);
}
function changeUser() {
if (!stdlib.setgid(pw.pw_gid)) {
stdio.write(io.stderr,'internal error\n');
io.exit(1);
return;
}
if (!stdlib.setuid(pw.pw_uid)) {
stdio.write(io.stderr,'internal error\n');
io.exit(1);
return;
}
var e = null;
if (preserve) {
e = structuredClone(env);
}else{
e = {};
e.USER = pw.pw_name;
e.PWD = pw.pw_dir;
e.HOME = pw.pw_dir;
e.SHELL = pw.pw_shell;
}
if (shell == null)
shell = e.SHELL;
var args = [shell];
var r = stdlib.exec(shell,args,e,io);
if (r == 0)
return;
stdio.write(io.stderr,'internal error\n');
io.exit(1);
}
function doLogin(str) {
term.ttyctrl('echo',true);
if (!auth.checkpassuid(pw.pw_uid,str)) {
stdio.printf('authentication error\n');
io.exit(1);
return;
}
changeUser();
}
function preLogin(str) {
if (str == 'no') {
io.exit(0);
return;
}
if (str != 'yes') {
cookies();
return;
}
if (stdlib.getuid() != 0) {
stdio.write(io.stdout,'password: ');
term.ttyctrl('echo',false);
if (!stdio.read(io.stdin,doLogin)) {
term.ttyctrl('echo',true);
stdio.write(io.stderr,'authentication error\n');
io.exit(1);
}
return;
}
changeUser();
}
function cookies() {
clite.cookienotice();
stdio.printf("Enter 'yes' to agree, or 'no' to cancel: ");
if (!stdio.read(io.stdin,preLogin)) {
stdio.write(io.stderr,'internal error\n');
io.exit(1);
}
}
function main(args) {
var user = 'root';
var shellnext = false;
var accept = false;
for (var i=1; i<args.length; i++) {
if (shellnext == true) {
shell = args[i];
shellnext = false;
}else if (args[i] == '-') {
preserve = false;
}else if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
case 'l':
preserve = false;
break;
case 'm':
preserve = true;
break;
case 's':
shellnext = true;
break;
case 'c':
accept = true;
break;
default:
stdio.fprintf(io.stderr,'unknown argument: -%c\n',args[i][j]);
}
}
}else{
user = args[i];
}
}
if (shellnext) {
stdio.write(io.stderr,'invalid arguments\n');
return 1;
}
if (!user) {
stdio.write(io.stderr,'no user specified\n');
return 1;
}
pw = stdlib.getpwnam(user);
if (!pw) {
stdio.fprintf(io.stderr,'invalid user: %s\n',user);
return 1;
}
if (!stdio.isatty(io.stdin)) {
stdio.write(io.stderr,'internal error\n');
return 1;
}
if (accept || user == 'root' || user == 'guest') {
preLogin('yes');
return null;
}
cookies();
return null;
}
return main(args);
},true);
if (window.location.protocol == 'file:')
clite.commands.load('exp',function(args,env,io) {
var stdio = io.include('stdio');

View file

@ -7,7 +7,7 @@ var clite = {
},
includes:{
// a list of program/command files to load
prog:['commands.js','shell.js','vi.js'],
prog:['commands.js','shell.js','vi.js','user.js'],
// a list of library files to load
libs:['libclite.js','libcurses.js','libstd.js','libstdio.js','libterm.js','libtime.js','libauth.js']
},
@ -349,17 +349,17 @@ cat /etc/greeting
function loadCommands(nextfn) {
clite.commands = {
data:null,
load:function(name,fn,setug) {
load:function(name,fn,suid,sgid) {
vfsapi.mkFile(0,'/bin/'+name);
var f = vfsapi.getNode(0,'/bin/'+name);
if (!f)
return;
f.mode = clite.lib.modestr('-rwxr-xr-x');
f.data.content = fn;
if (typeof setug === 'boolean' && setug == true) {
if (typeof suid === 'boolean' && suid == true)
f.mode |= clite.io.modes.S_ISUID;
if (typeof sgid === 'boolean' && sgid == true)
f.mode |= clite.io.modes.S_ISGID;
}
vfsapi.mkFile(0,'/usr/src/'+name+'.js');
f = vfsapi.getNode(0,'/usr/src/'+name+'.js');
if (!f)
@ -804,7 +804,8 @@ clite.proc = {
return false;
if (!force && proc.uid != 0)
return false;
proc.gpid = addGroup(pid);
addGroup(pid);
proc.gpid = pid;
proc.ruid = uid;
return true;
}
@ -1048,6 +1049,26 @@ cat -l /usr/share/introduction
pw_shell:udata.env.SHELL // Program to use as shell.
});
}
clite.user.removePW = function(pid,uid) {
if (clite.proc.getRUID(pid) != 0)
return false;
loadUsers();
var ind = -1;
for (var i=0; i<data.users.length; i++) {
if (data.users[i].uid == uid)
ind = i;
}
if (ind<0)
return false;
data.users.splice(ind,1);
var udata = getUser(uid);
if (udata)
return false;
saveUsers();
return true;
}
clite.user.setPWData = function(pid,pw) {
if (typeof pw.pw_name !== 'string')
return false;
@ -1127,6 +1148,26 @@ cat -l /usr/share/introduction
gr_mem:gdata.users // array of member names.
});
}
clite.user.removeGR = function(pid,gid) {
if (clite.proc.getRUID(pid) != 0)
return false;
loadUsers();
var ind = -1;
for (var i=0; i<data.groups.length; i++) {
if (data.groups[i].gid == gid)
ind = i;
}
if (ind<0)
return false;
data.groups.splice(ind,1);
var gdata = getUser(gid);
if (gdata)
return false;
saveUsers();
return true;
}
clite.user.setGRData = function(pid,gr) {
if (typeof gr.gr_name !== 'string')
return false;
@ -1640,12 +1681,57 @@ clite.io = {
var fd = getFileDesPre(pid,path,true);
return clite.io.fchmod(pid,fd,mode);
}
clite.io.chown = function(pid,path,uid,gid) {
var fd = getFileDesPre(pid,path,true);
if (!fd)
return false;
var euid = clite.proc.getUID(pid);
if (euid != 0) {
if (fd.node.uid != euid)
return false;
if (uid > -1 && uid != euid)
return false;
var egid = clite.proc.getGID(pid);
if (gid > -1 && gid != egid)
return false;
}
if (uid > -1)
fd.node.uid = uid;
if (gid > -1)
fd.node.gid = gid;
return true;
}
clite.io.lchown = function(pid,path,uid,gid) {
var fd = getFileDesPre(pid,path,false);
if (!fd)
return false;
var euid = clite.proc.getUID(pid);
if (euid != 0) {
if (fd.node.uid != euid)
return false;
if (uid > -1 && uid != euid)
return false;
var egid = clite.proc.getGID(pid);
if (gid > -1 && gid != egid)
return false;
}
if (uid > -1)
fd.node.uid = uid;
if (gid > -1)
fd.node.gid = gid;
return true;
}
clite.io.isatty = function(pid,fd) {
return fd.node.data.istty;
}
clite.io.mkdir = function(pid,path,mode) {
if (typeof mode != 'number')
return false;
if (typeof mode != 'number' || mode == 0) {
mode = clite.io.modes.S_IRWXU|clite.io.modes.S_IRGRP|clite.io.modes.S_IXGRP;
}
mode &= ~clite.io.modes.S_IFMT;
mode |= clite.io.modes.S_IFDIR;
var uid = clite.proc.getUID(pid);

View file

@ -7,10 +7,16 @@ return Object.create({
setpwentry:function(pw) {
return clite.user.setPWData(io.pid,pw);
},
removepw:function(uid) {
return clite.user.removePW(io.pid,uid);
},
setgrentry:function(gr) {
return clite.user.setGRData(io.pid,gr);
},
removegr:function(gid) {
return clite.user.removeGR(io.pid,gid);
},
setpassuid:function(uid, pass) {
return clite.user.setPasswd(io.pid,uid,pass);

View file

@ -58,14 +58,14 @@ return Object.create({
return clite.proc.getUID(io.pid);
},
setuid:function(uid) {
if (!clite.proc.setUID(io.pid,uid,false))
if (!clite.proc.setRUID(io.pid,uid,false))
return false;
return clite.proc.setRUID(io.pid,uid,false);
return clite.proc.setUID(io.pid,uid,false);
},
setreuid:function(ruid,euid) {
if (!clite.proc.setUID(io.pid,euid,false))
if (!clite.proc.setRUID(io.pid,ruid,false))
return false;
return clite.proc.setRUID(io.pid,ruid,false);
return clite.proc.setUID(io.pid,euid,false);
},
// gets the passwd file data for a user based on their uid
@ -98,14 +98,14 @@ return Object.create({
return clite.proc.getGID(io.pid);
},
setgid:function(gid) {
if (!clite.proc.setGID(io.pid,gid,false))
if (!clite.proc.setRGID(io.pid,gid,false))
return false;
return clite.proc.setRGID(io.pid,gid,false);
return clite.proc.setGID(io.pid,gid,false);
},
setregid:function(rgid,egid) {
if (!clite.proc.setGID(io.pid,egid,false))
if (!clite.proc.setRGID(io.pid,rgid,false))
return false;
return clite.proc.setRGID(io.pid,rgid,false);
return clite.proc.setGID(io.pid,egid,false);
},
endgrent:function() {},
getgrent:function() {

View file

@ -90,6 +90,13 @@ return Object.create({
return clite.io.fchmod(io.pid,fd,mode);
},
chown:function(path,uid,gid) {
return clite.io.chown(io.pid,path,uid,gid);
},
lchown:function(path,uid,gid) {
return clite.io.lchown(io.pid,path,uid,gid);
},
isatty:function(fd) {
return clite.io.isatty(io.pid,fd);
},

414
clite/user.js Normal file
View file

@ -0,0 +1,414 @@
clite.commands.data = function() {
clite.commands.load('su',function(args,env,io) {
var stdio = io.include('stdio');
var stdlib = io.include('stdlib');
var clite = io.include('clite');
var auth = io.include('auth');
var term = io.include('term');
var pw = null;
var preserve = true;
var shell = null;
function help() {
stdio.printf(`
su - switch users
Usage: su [OPTION]
Options:
-? Print this help information
-l Simulate a full login
-m Preserve the environment
-s <sh> Use the shell at <sh>
-c Automatically accept cookies when switching users
`);
}
function changeUser() {
if (!stdlib.setgid(pw.pw_gid)) {
stdio.write(io.stderr,'internal error (1)\n');
io.exit(1);
return;
}
if (!stdlib.setuid(pw.pw_uid)) {
stdio.write(io.stderr,'internal error (2)\n');
io.exit(1);
return;
}
var e = null;
if (preserve) {
e = structuredClone(env);
}else{
e = {};
e.USER = pw.pw_name;
e.PWD = pw.pw_dir;
e.HOME = pw.pw_dir;
e.SHELL = pw.pw_shell;
}
if (shell == null)
shell = e.SHELL;
var args = [shell];
var r = stdlib.exec(shell,args,e,io);
if (r == 0)
return;
stdio.write(io.stderr,'internal error (3)\n');
io.exit(1);
}
function doLogin(str) {
term.ttyctrl('echo',true);
if (!auth.checkpassuid(pw.pw_uid,str)) {
stdio.printf('authentication error\n');
io.exit(1);
return;
}
changeUser();
}
function preLogin(str) {
if (str == 'no') {
io.exit(0);
return;
}
if (str != 'yes') {
cookies();
return;
}
if (stdlib.getuid() != 0) {
stdio.write(io.stdout,'password: ');
term.ttyctrl('echo',false);
if (!stdio.read(io.stdin,doLogin)) {
term.ttyctrl('echo',true);
stdio.write(io.stderr,'authentication error\n');
io.exit(1);
}
return;
}
changeUser();
}
function cookies() {
clite.cookienotice();
stdio.printf("Enter 'yes' to agree, or 'no' to cancel: ");
if (!stdio.read(io.stdin,preLogin)) {
stdio.write(io.stderr,'internal error\n');
io.exit(1);
}
}
function main(args) {
var user = 'root';
var shellnext = false;
var accept = false;
for (var i=1; i<args.length; i++) {
if (shellnext == true) {
shell = args[i];
shellnext = false;
}else if (args[i] == '-') {
preserve = false;
}else if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
case 'l':
preserve = false;
break;
case 'm':
preserve = true;
break;
case 's':
shellnext = true;
break;
case 'c':
accept = true;
break;
default:
stdio.fprintf(io.stderr,'unknown argument: -%c\n',args[i][j]);
}
}
}else{
user = args[i];
}
}
if (shellnext) {
stdio.write(io.stderr,'invalid arguments\n');
return 1;
}
if (!user) {
stdio.write(io.stderr,'no user specified\n');
return 1;
}
pw = stdlib.getpwnam(user);
if (!pw) {
stdio.fprintf(io.stderr,'invalid user: %s\n',user);
return 1;
}
if (!stdio.isatty(io.stdin)) {
stdio.write(io.stderr,'internal error\n');
return 1;
}
if (accept || user == 'root' || user == 'guest') {
preLogin('yes');
return null;
}
cookies();
return null;
}
return main(args);
},true);
clite.commands.load('user',function(args,env,io) {
var stdio = io.include('stdio');
var stdlib = io.include('stdlib');
var auth = io.include('auth');
var shell = null;
var group = null;
function help() {
stdio.printf(`
user - manage user accounts
Usage: user <OPTION> [username]
Options:
-? Print this help information
-a Add a new user account
-d Delete a user account
-s <SH> Set the user's default shell to <SH>
-g <GR> Set the user's primary group to <GR>
`);
}
function validateName(n) {
const char = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_';
if (n.length < 2 || n.length > 12)
return false;
if (char.indexOf(n[0]) < 0)
return false;
for (var i=0; i<n.length; i++) {
if (chars.indexOf(n[i]) < 0)
return false;
}
return true;
}
function addUser(user) {
var gid = -1;
if (!validateName(user)) {
stdio.fprintf(io.stderr,'invalid user name: %s\n',user);
return 1;
}
// check user doesn't already exit
var pw = stdlib.getpwnam(user);
if (pw != null) {
stdio.fprintf(io.stderr,'user already exists: %s\n',user);
return 1;
}
// if group is set, check group exists
if (group != null) {
if (!validateName(group)) {
stdio.fprintf(io.stderr,'invalid group name: %s\n',user);
return 1;
}
let gr = stdlib.getgrnam(group);
if (gr == null) {
stdio.fprintf(io.stderr,'cannot assign user to nonexistant group: %s\n',group);
return 1;
}
gr.gr_mem.push(user);
if (!auth.setgrentry(gr)) {
stdio.fprintf(io.stderr,'could not add user to group: %s\n',group);
return 1;
}
gid = gr.gr_gid;
// if group is not set, create a group with the user's name
}else{
let gr = stdlib.getgrnam(user);
if (gr != null) {
stdio.fprintf(io.stderr,'group already exists: %s\n',user);
return 1;
}
gr = {
gr_name:user,
gr_gid:-1,
gr_mem:[user]
};
if (!auth.setgrentry(gr)) {
stdio.fprintf(io.stderr,'could not create group: %s\n',user);
return 1;
}
gr = stdlib.getgrnam(user);
if (gr == null) {
stdio.fprintf(io.stderr,'error creating group: %s\n',user);
return 1;
}
gid = gr.gr_gid;
}
if (gid < 0) {
stdio.fprintf(io.stderr,'error in group setting\n');
return 1;
}
if (shell == null)
shell = '/bin/sh';
// create passwd record
pw = {
pw_name:user,
pw_uid:-1,
pw_gid:gid,
pw_dir:'/usr/home/'+user,
pw_shell:shell
};
if (!auth.setpwentry(pw)) {
stdio.fprintf(io.stderr,'could not create user: %s\n',user);
return 1;
}
pw = stdlib.getpwnam(user);
if (pw == null) {
stdio.fprintf(io.stderr,'error creating user: %s\n',user);
return 1;
}
// create home directory
// set ownership and permission
if (!stdio.mkdir(pw.pw_dir,stdio.modes.S_IRWXU)) {
stdio.fprintf(io.stderr,'could not create home directory: %s\n',pw.pw_dir);
return 1;
}
if (!stdio.chown(pw.pw_dir,pw.pw_uid,pw.pw_gid)) {
stdio.fprintf(io.stderr,'error creating home directory: %s\n',pw.pw_dir);
return 1;
}
// password?
return 0;
}
function delUser(user) {
var pw = stdlib.getpwnam(user);
if (pw == null) {
stdio.fprintf(io.stderr,'user does not exists: %s\n',user);
return 1;
}
// check user is not current user
if (pw.pw_uid == stdlib.getuid() || pw.pw_uid == stdlib.geteuid()) {
stdio.fprintf(io.stderr,'cannot delete current user: %s\n',user);
return 1;
}
// delete passwd entry
if (!auth.removepw(pw.pw_uid)) {
stdio.fprintf(io.stderr,'could not delete user: %s\n',user);
return 1;
}
// delete home directory?
return 0;
}
function main(args) {
let add = false;
let rem = false;
let user = null;
let shellnext = false;
let groupnext = false;
for (var i=1; i<args.length; i++) {
if (shellnext == true) {
shell = args[i];
shellnext = false;
}else if (groupnext == true) {
group = args[i];
groupnext = false;
}else if (args[i][0] == '-') {
for (var j=1; j<args[i].length; j++) {
switch (args[i][j]) {
case '?':
help();
return 0;
break;
case 'a':
add = true;
break;
case 'd':
rem = true;
break;
case 's':
shellnext = true;
break;
case 'g':
groupnext = true;
break;
default:
stdio.fprintf(io.stderr,'unknown argument: -%c\n',args[i][j]);
}
}
}else{
user = args[i];
}
}
if (user == null) {
stdio.write(io.stderr,'no user specified\n');
return 1;
}
if (user == 'guest' || user == 'root') {
stdio.write(io.stderr,'the guest and root accounts cannot be modified\n');
return 1;
}
if (add)
return addUser(user);
if (rem)
return delUser(user);
stdio.write(io.stderr,'no option specified\n');
return 1;
}
return main(args);
},true);
}