I have a sudden urge to remove the only expect script on my systems. To do that I have to be able to set passwords with Solaris passwd. I've tried this once before, and failed.
Short version of the expect script:
----8<------------------------------------- spawn /usr/bin/passwd -r nis $uname expect { -re assword: { send "$password\n" } } expect { -re assword: { send "$password\n" } } expect { eof { wait } } ----8<-------------------------------------
In pike that should be something like this:
----8<------------------------------------- #!/usr/bin/env pike
array(Stdio.File) make_ptypair() { Stdio.File pf = Stdio.File(), tf = Stdio.File(); foreach(get_dir("/dev/"), string pty_name) if(pty_name[..2]=="pty" && pf->open("/dev/"+pty_name, "rw")) { if(tf->open("/dev/t"+pty_name[1..], "rw")) return ({ pf, tf }); pf->close(); } return 0; }
int main() { string luser = "victim"; array(Stdio.File) f = make_ptypair(); if(!f) { werror("No PTYs available.\n"); return 1; } object p = Process.create_process( ({ "/usr/bin/passwd", luser }), ([ "setsid":f[1] ]) ); f[1]->close(); f[0]->write("bogus\n"); f[0]->write("bogus\n"); return p->wait(); } ----8<-------------------------------------
But no, that still gives the password question on the command line:
[root] ./setsid.pike New password:
Any ideas?
Passwd requires a tty?
It's not enough just working with stderr and stdin?
object f=Stdio.File(); object g=Stdio.File(); object p = Process.create_process( ({ "/usr/bin/passwd", luser }), ([ "stderr":f->pipe(), "stdin":g->pipe() ]) ); g->write("bogus\n"); g->write("bogus\n"); g->close();
...f->read()...
/ Mirar
Previous text:
2003-02-15 14:25: Subject: setsid
I have a sudden urge to remove the only expect script on my systems. To do that I have to be able to set passwords with Solaris passwd. I've tried this once before, and failed.
Short version of the expect script:
----8<------------------------------------- spawn /usr/bin/passwd -r nis $uname expect { -re assword: { send "$password\n" } } expect { -re assword: { send "$password\n" } } expect { eof { wait } } ----8<-------------------------------------
In pike that should be something like this:
----8<------------------------------------- #!/usr/bin/env pike
array(Stdio.File) make_ptypair() { Stdio.File pf = Stdio.File(), tf = Stdio.File(); foreach(get_dir("/dev/"), string pty_name) if(pty_name[..2]=="pty" && pf->open("/dev/"+pty_name, "rw")) { if(tf->open("/dev/t"+pty_name[1..], "rw")) return ({ pf, tf }); pf->close(); } return 0; }
int main() { string luser = "victim"; array(Stdio.File) f = make_ptypair(); if(!f) { werror("No PTYs available.\n"); return 1; } object p = Process.create_process( ({ "/usr/bin/passwd", luser }), ([ "setsid":f[1] ]) ); f[1]->close(); f[0]->write("bogus\n"); f[0]->write("bogus\n"); return p->wait(); } ----8<-------------------------------------
But no, that still gives the password question on the command line:
[root] ./setsid.pike New password:
Any ideas?
/ Peter Bortas
No. That would have been to simple. That is how I handle all the normal commands.
/ Peter Bortas
Previous text:
2003-02-15 14:31: Subject: setsid
Passwd requires a tty?
It's not enough just working with stderr and stdin?
object f=Stdio.File(); object g=Stdio.File(); object p = Process.create_process( ({ "/usr/bin/passwd", luser }), ([ "stderr":f->pipe(), "stdin":g->pipe() ]) ); g->write("bogus\n"); g->write("bogus\n"); g->close();
...f->read()...
/ Mirar
Oh. You don't need to do both, then? I'm not sure it finds the tty on it's own, isn't that usually done by staring at stdin really hard?
/ Mirar
Previous text:
2003-02-15 14:43: Subject: setsid
No. That would have been to simple. That is how I handle all the normal commands.
/ Peter Bortas
Hmm. I don't think so. Not that it stopped me from testing. :)
TTY stuff is not something I've looked closely at before either.
In make_ptypair a free PTY and and the corresponding TTY is aquired and returned as f[0] and f[1] respectively.
The controlling terminal is set to the (slave) TTY with setsid.
Around here it gets fuzzy. I wounder if the is a UNIX course on Uni that handles TTY/PTY programming.
/ Peter Bortas
Previous text:
2003-02-15 14:52: Subject: setsid
..."stdin":f[1], "stdout":f[0]...
or something like that. Possibly? I'm not too good with TTYs. :)
/ Mirar
The unix book contains some information.
Are you sure that f[0] is the slave side of the pty pair?
/ Niels Möller ()
Previous text:
2003-02-15 15:28: Subject: setsid
Hmm. I don't think so. Not that it stopped me from testing. :)
TTY stuff is not something I've looked closely at before either.
In make_ptypair a free PTY and and the corresponding TTY is aquired and returned as f[0] and f[1] respectively.
The controlling terminal is set to the (slave) TTY with setsid.
Around here it gets fuzzy. I wounder if the is a UNIX course on Uni that handles TTY/PTY programming.
/ Peter Bortas
Both should be f[1]. (Remember that ttys are bidirectional.) f[0] is the controlling end.
/ Marcus Comstedt (ACROSS) (Hail Ilpalazzo!)
Previous text:
2003-02-15 14:52: Subject: setsid
..."stdin":f[1], "stdout":f[0]...
or something like that. Possibly? I'm not too good with TTYs. :)
/ Mirar
The standard way for a process to get to its controlling tty is to use open "/dev/tty".
/ Niels Möller ()
Previous text:
2003-02-15 14:45: Subject: setsid
Oh. You don't need to do both, then? I'm not sure it finds the tty on it's own, isn't that usually done by staring at stdin really hard?
/ Mirar
Yes, it seamed very nice, but I only got it to work on Linux. On Solaris grantpt() says "grantpt failed: No child processes". According to truss pike doesn't set up it's own sigchild handler, so that shouldn't be it. The man page for grantpt also mentions that it runs some setuid root program as a possible reason for failures, but again according to truss it doesn't seem to get to that.
/ Peter Bortas
Previous text:
2003-02-15 21:08: Subject: setsid
/dev/ptmx may be cheating, but it's the only easy way to create pty pairs without introducing security holes.
/ Niels Möller ()
The traditioal way of using /dev/ptmx (I'm not doing it quite like that myself in lsh, so I hope I get this right) is as follows:
master = open("/dev/ptmx") fork()
in the child: setuid() // The standard grantpt function depends on the // process uid, so if you want to change uid, do that first. grantpt(master) unlockpt(master) setsid() // Loses old controlling terminal slave = open(ptsname(master) // On sys5, makes the slave our // controlling terminal
for sys5, setup STREAMS: ioctl(slave, I_PUSH, "ptem") ioctl(slave, I_PUSH, "ldterm")
for BSD, where opening the slave didn't make it our controlling terminal ioctl(fd, TIOCSCTTY, NULL)
close(master) dup fd:s around exec new program
in the parent: read and write the master side of the tty.
As an extra complication, at least on Solaris you'll get i/o errors if you try to do any i/o before the child process has opened and set up the tty. So you may need to either move some of the slave code before fork (no problem unless you want to change uid...), or use some extra syncronization.
/ Niels Möller ()
Previous text:
2003-02-15 21:21: Subject: setsid
Yes, it seamed very nice, but I only got it to work on Linux. On Solaris grantpt() says "grantpt failed: No child processes". According to truss pike doesn't set up it's own sigchild handler, so that shouldn't be it. The man page for grantpt also mentions that it runs some setuid root program as a possible reason for failures, but again according to truss it doesn't seem to get to that.
/ Peter Bortas
Is there some compelling reason not to do
master = open("/dev/ptmx") grantpt(master)
before fork(), if you aren't going to change uid? Because that is what I do. Doing the grantpt() in the child requires modification* of pike.
* I have on my todo to make a Process.create_ptypair() or something, but not today.
/ Peter Bortas
Previous text:
2003-02-15 21:37: Subject: setsid
The traditioal way of using /dev/ptmx (I'm not doing it quite like that myself in lsh, so I hope I get this right) is as follows:
master = open("/dev/ptmx") fork()
in the child: setuid() // The standard grantpt function depends on the // process uid, so if you want to change uid, do that first. grantpt(master) unlockpt(master) setsid() // Loses old controlling terminal slave = open(ptsname(master) // On sys5, makes the slave our // controlling terminal
for sys5, setup STREAMS: ioctl(slave, I_PUSH, "ptem") ioctl(slave, I_PUSH, "ldterm")
for BSD, where opening the slave didn't make it our controlling terminal ioctl(fd, TIOCSCTTY, NULL)
close(master) dup fd:s around exec new program
in the parent: read and write the master side of the tty.
As an extra complication, at least on Solaris you'll get i/o errors if you try to do any i/o before the child process has opened and set up the tty. So you may need to either move some of the slave code before fork (no problem unless you want to change uid...), or use some extra syncronization.
/ Niels Möller ()
As far as I can tell, that should work.
(it even works if you want to change uid, using the modified version of grantpt below)
/* Sets the permissions on the slave pty suitably for use by USER. * This function is derived from the grantpt function in * sysdeps/unix/grantpt.c in glibc-2.1. */
static int pty_check_permissions(const char *name, uid_t user) { struct stat st; struct group *grp; gid_t tty_gid;
if (stat(name, &st) < 0) return 0;
/* Make sure that the user owns the device. */ if ( (st.st_uid != user) && (chown(name, user, st.st_gid) < 0) ) return 0;
/* Points to static area */ grp = getgrnam(TTY_GROUP);
if (grp) tty_gid = grp->gr_gid; else { /* If no tty group is found, use the server's gid */ werror("lshd: server_pty.c: No tty group found.\n"); tty_gid = getgid(); }
if ( (st.st_gid != tty_gid) && (chown(name, user, tty_gid) < 0)) return 0;
/* Make sure the permission mode is set to readable and writable * by the owner, and writable by the group. */
if ( ((st.st_mode & ACCESSPERMS) != (S_IRUSR | S_IWUSR | S_IWGRP)) && (chmod(name, S_IRUSR | S_IWUSR | S_IWGRP) < 0) ) return 0;
/* Everything is fine */ return 1; }
/* Returns the name of the slave tty, or NULL on failure. */
const char * pty_grantpt_uid(int master, uid_t user) { uid_t me = getuid(); if (me == user) { /* Use standard grantpt call */ if (grantpt(master) < 0) return NULL;
return ptsname(master); } else { /* Set up permissions for user */
/* Pointer to static area */ char *name = ptsname(master); return (pty_check_permissions(name, user) ? name : NULL); } }
/ Niels Möller ()
Previous text:
2003-02-15 21:52: Subject: setsid
Is there some compelling reason not to do
master = open("/dev/ptmx") grantpt(master)
before fork(), if you aren't going to change uid? Because that is what I do. Doing the grantpt() in the child requires modification* of pike.
- I have on my todo to make a Process.create_ptypair() or something,
but not today.
/ Peter Bortas
- This function is derived from the grantpt function in
- sysdeps/unix/grantpt.c in glibc-2.1. */
That makes the actual code unusable by me, but I get the general idea. Thanks.
/ Peter Bortas
Previous text:
2003-02-15 22:03: Subject: setsid
As far as I can tell, that should work.
(it even works if you want to change uid, using the modified version of grantpt below)
/* Sets the permissions on the slave pty suitably for use by USER.
- This function is derived from the grantpt function in
- sysdeps/unix/grantpt.c in glibc-2.1. */
static int pty_check_permissions(const char *name, uid_t user) { struct stat st; struct group *grp; gid_t tty_gid;
if (stat(name, &st) < 0) return 0;
/* Make sure that the user owns the device. */ if ( (st.st_uid != user) && (chown(name, user, st.st_gid) < 0) ) return 0;
/* Points to static area */ grp = getgrnam(TTY_GROUP);
if (grp) tty_gid = grp->gr_gid; else { /* If no tty group is found, use the server's gid */ werror("lshd: server_pty.c: No tty group found.\n"); tty_gid = getgid(); }
if ( (st.st_gid != tty_gid) && (chown(name, user, tty_gid) < 0)) return 0;
/* Make sure the permission mode is set to readable and writable
- by the owner, and writable by the group. */
if ( ((st.st_mode & ACCESSPERMS) != (S_IRUSR | S_IWUSR | S_IWGRP)) && (chmod(name, S_IRUSR | S_IWUSR | S_IWGRP) < 0) ) return 0;
/* Everything is fine */ return 1; }
/* Returns the name of the slave tty, or NULL on failure. */
const char * pty_grantpt_uid(int master, uid_t user) { uid_t me = getuid(); if (me == user) { /* Use standard grantpt call */ if (grantpt(master) < 0) return NULL;
return ptsname(master);
} else { /* Set up permissions for user */
/* Pointer to static area */ char *name = ptsname(master); return (pty_check_permissions(name, user) ? name : NULL);
} }
/ Niels Möller ()
pike-devel@lists.lysator.liu.se