/* $Id: ptyd.C,v 1.9 2001/12/06 19:57:15 kaminsky Exp $ */ /* * * Copyright (C) 2001 Eric Peterson (ericp@lcs.mit.edu) * Copyright (C) 2001 Michael Kaminsky (kaminsky@lcs.mit.edu) * Copyright (C) 2000 Charles Blake (cblake@lcs.mit.edu) * Copyright (C) 1999 David Mazieres (dm@uun.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "arpc.h" #include "sfsmisc.h" #include "rex_prot.h" #include "ptyd.h" #include #if !defined(SYSV) && !defined(linux) && !defined(__QNX__) #include #endif static const char ttygrpname[] = "tty"; static gid_t ttygrp = (gid_t) -1; struct ptyclient; struct pty { ptyclient *const c; const str path; ihash_entry link; pty (ptyclient *cc, const str &p, const str &host); ~pty (); }; struct ptyclient { ptr x; ptr s; const uid_t uid; ihash ptys; ptyclient::ptyclient (ref xx, uid_t u) : x (xx), s (asrv::alloc (x, ptyd_prog_1)), uid (u) {} ~ptyclient (); void dispatch (svccb *sbp); }; #if defined PATH_SESSREG #if !defined(SYSV) && !defined(linux) && !defined(__QNX__) static int /* The following code is adapted from the OpenBSD source to ttyslot (3). */ myttyslot(const char *name) { struct ttyent *ttyp; int slot; char *p; setttyent (); if ((p = strrchr (name, '/'))) ++p; else p = const_cast (name); for (slot = 1; (ttyp = getttyent ()); ++slot) if (!strcmp (ttyp->ty_name, p)) { endttyent (); return slot; } endttyent (); return 0; } #endif /* #if !defined(SYSV) && !defined(linux) && !defined(__QNX__) */ static int sessreg (int add, const uid_t u, const char *tty, const char *host) { struct passwd *pw; pid_t pid; char *tmp; if (!(pw = getpwuid (u))) return -1; vec argv; argv.push_back ("sessreg"); if (add) argv.push_back ("-a"); else argv.push_back ("-d"); argv.push_back ("-l"); if (tty && !strncmp ("/dev/", tty, 5) && tty[5]) tmp = const_cast (&tty[5]); else { tmp = strrchr (tty, '/'); tmp++; } if (tmp) argv.push_back (tmp); else argv.push_back ("?"); if (host) { argv.push_back ("-h"); argv.push_back (const_cast (host)); } #if !defined(SYSV) && !defined(linux) && !defined(__QNX__) int slot = myttyslot (tty); if (slot > 0) { str s = strbuf () << slot; argv.push_back ("-s"); argv.push_back (const_cast (s.cstr ())); } #endif argv.push_back (pw->pw_name); argv.push_back (NULL); if (!(pid = fork ())) { int ret = execv (PATH_SESSREG, argv.base ()); if (ret < 0) warn << "Exec error: " << strerror (errno) << "\n"; } /* XXX: What happens if "sessreg" isn't found? Crash in config or here? */ /* wait (pid); -- we probably don't care about status */ #ifdef HAVE_GETPWUID_ALLOCS free (pw); #endif return 0; } #else /* !PATH_SESSREG */ inline static int sessreg (int add, const uid_t u, const char *tty, const char *host) { return 0; } #endif /* !PATH_SESSREG */ pty::pty (ptyclient *cc, const str &p, const str &host) : c (cc), path (p) { sessreg (1, c->uid, path, host); chmod (path, 0600); chown (path, c->uid, ttygrp); c->ptys.insert (this); } pty::~pty () { sessreg (0, c->uid, path, 0); chown (path, 0, ttygrp == (gid_t) -1 ? 0 : ttygrp); chmod (path, 0666); c->ptys.remove (this); } static void delete_pty (pty *p) { delete p; } ptyclient::~ptyclient () { ptys.traverse (wrap (delete_pty)); } void ptyclient::dispatch (svccb *sbp) { if (!sbp) { delete this; return; } switch (sbp->proc ()) { case PTYD_NULL: sbp->reply (NULL); break; case PTYD_PTY_ALLOC: { int fd; str path; pty_alloc_res res (0); if (pty_alloc (&fd, &path)) { New pty (this, path, *sbp->template getarg ()); x->sendfd (fd); *res.path = path; } else res.set_err (errno); sbp->reply (&res); break; } case PTYD_PTY_FREE: { int32_t res = 0; if (pty *p = ptys[*sbp->template getarg ()]) delete p; else res = ENOENT; sbp->reply (&res); break; } default: sbp->reject (PROC_UNAVAIL); break; } } static void getcon (ptr x, const authunix_parms *aup) { if (!x || x->ateof ()) fatal ("suidserv failed\n"); ptyclient *pcp = New ptyclient (x, aup->aup_uid); pcp->s->setcb (wrap (pcp, &ptyclient::dispatch)); } int main (int argc, char **argv) { setprogname (argv[0]); sfsconst_init (); if (group *gr = getgrnam (ttygrpname)) ttygrp = gr->gr_gid; sfs_suidserv ("ptyd", wrap (getcon)); amain (); }