/* $Id: sfsserv.C,v 1.6 2001/06/28 04:47:23 dm Exp $ */ /* * * Copyright (C) 2000 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 "sfsserv.h" #include ptr getauthclnt () { static ptr authxprt; static ptr authclnt; if (authclnt && !authxprt->ateof ()) return authclnt; int fd = suidgetfd ("authserv"); if (fd < 0) { authxprt = NULL; authclnt = NULL; return NULL; } authclnt = aclnt::alloc (authxprt = axprt_stream::alloc (fd), sfsauth_program_1); return authclnt; } sfsserv::sfsserv (ref xxc, ptr xx) : xc (xxc), x (xx ? xx : implicit_cast > (xxc)), destroyed (New refcounted (false)), sfssrv (asrv::alloc (x, sfs_program_1, wrap (this, &sfsserv::dispatch))), seqstate (128), authid_valid (false) { authtab.push_back (); credtab.push_back (); } sfsserv::~sfsserv () { *destroyed = true; } u_int32_t sfsserv::authalloc () { if (authfreelist.size ()) return authfreelist.pop_back (); if (authtab.size () >= 0x10000) return 0; authtab.push_back (); credtab.push_back (); return authtab.size () - 1; } void sfsserv::authfree (size_t n) { if (n && n < authtab.size () && authtab[n]) { authtab[n] = NULL; credtab[n].set_type (SFS_NOCRED); authfreelist.push_back (n); } } void sfsserv::dispatch (svccb *sbp) { if (!sbp) return; switch (sbp->proc ()) { case SFSPROC_NULL: sbp->reply (NULL); break; case SFSPROC_CONNECT: sfs_connect (sbp); break; case SFSPROC_ENCRYPT: sfs_encrypt (sbp); break; case SFSPROC_GETFSINFO: sfs_getfsinfo (sbp); break; case SFSPROC_LOGIN: sfs_login (sbp); break; case SFSPROC_LOGOUT: sfs_logout (sbp); break; case SFSPROC_IDNAMES: sfs_idnames (sbp); break; case SFSPROC_IDNUMS: sfs_idnums (sbp); break; case SFSPROC_GETCRED: sfs_getcred (sbp); break; default: sfs_badproc (sbp); return; } } void sfsserv::sfs_connect (svccb *sbp) { if (cd || authid_valid) { sbp->reject (PROC_UNAVAIL); return; } cd.alloc (); cd->ci = *sbp->template getarg (); cd->cr.set_status (SFS_OK); cd->cr.reply->charge.bitcost = sfs_hashcost; rnd.getbytes (cd->cr.reply->charge.target.base (), charge.target.size ()); sk = doconnect (&cd->ci, &cd->cr.reply->servinfo); if (!sk && !cd->cr.status) cd->cr.set_status (SFS_NOSUCHHOST); sbp->reply (&cd->cr); } static sfs_service ci2service (const sfs_connectinfo &ci) { switch (ci.civers) { case 4: return ci.ci4->service; case 5: return ci.ci5->service; default: return sfs_service (0); } } void sfsserv::sfs_encrypt (svccb *sbp) { if (!cd || cd->cr.status) { sbp->reject (PROC_UNAVAIL); return; } sfs_server_crypt (sbp, sk, cd->ci, cd->cr.reply->servinfo, &sessid, cd->cr.reply->charge, xc); sfs_hash hostid; bool hostid_ok = sfs_mkhostid (&hostid, cd->cr.reply->servinfo.host); assert (hostid_ok); sfs_get_authid (&authid, ci2service (cd->ci), cd->cr.reply->servinfo.host.hostname, &hostid, &sessid); authid_valid = true; cd.clear (); } static void sfs_login_cb (ref destroyed, sfsserv *srv, svccb *sbp, sfsauth_loginres *_resp, clnt_stat stat) { auto_ptr resp (_resp); if (stat || *destroyed) { if (stat) warn << "authserv: " << stat << "\n"; sbp->replyref (sfs_loginres (SFSLOGIN_ALLBAD)); return; } sfs_loginres res (resp->status); switch (resp->status) { case SFSLOGIN_OK: { if (resp->resok->authid != srv->authid || !srv->seqstate.check (resp->resok->seqno) || resp->resok->cred.type != SFS_UNIXCRED) { res.set_status (SFSLOGIN_BAD); break; } u_int32_t authno = srv->authalloc (); if (!authno) { warn << "ran out of authnos\n"; res.set_status (SFSLOGIN_BAD); break; } *res.authno = authno; sfs_unixcred &uc = *resp->resok->cred.unixcred; srv->authtab[authno] = authunixint_create ("localhost", uc.uid, uc.gid, uc.groups.size (), uc.groups.base ()); srv->credtab[authno] = resp->resok->cred; break; } case SFSLOGIN_MORE: *res.resmore = *resp->resmore; break; default: break; } sbp->reply (&res); } void sfsserv::sfs_login (svccb *sbp) { ptr c; if (!authid_valid || !(c = getauthclnt ())) { sbp->replyref (sfs_loginres (SFSLOGIN_ALLBAD)); return; } sfsauth_loginres *resp = New sfsauth_loginres; c->call (SFSAUTHPROC_LOGIN, sbp->template getarg (), resp, wrap (sfs_login_cb, destroyed, this, sbp, resp)); } void sfsserv::sfs_logout (svccb *sbp) { authfree (*sbp->template getarg ()); sbp->reply (NULL); } // XXX - MAJOR DEADLOCK PROBLEMS HERE // XXX - should never call getpw* or getgr* void sfsserv::sfs_idnames (svccb *sbp) { if (!getauth (sbp->getaui ())) { sbp->reject (AUTH_REJECTEDCRED); return; } ::sfs_idnums *argp = sbp->template getarg< ::sfs_idnums> (); ::sfs_idnames res; if (argp->uid != -1) if (struct passwd *p = getpwuid (argp->uid)) { res.uidname.set_present (true); *res.uidname.name = p->pw_name; } if (argp->gid != -1) if (struct group *g = getgrgid (argp->gid)) { res.gidname.set_present (true); *res.gidname.name = g->gr_name; } sbp->reply (&res); } // XXX - MAJOR DEADLOCK PROBLEMS HERE // XXX - should never call getpw* or getgr* void sfsserv::sfs_idnums (svccb *sbp) { if (!getauth (sbp->getaui ())) { sbp->reject (AUTH_REJECTEDCRED); return; } ::sfs_idnames *argp = sbp->template getarg< ::sfs_idnames> (); ::sfs_idnums res = { -1, -1 }; if (argp->uidname.present) if (struct passwd *p = getpwnam (argp->uidname.name->cstr ())) res.uid = p->pw_uid; if (argp->gidname.present) if (struct group *g = getgrnam (argp->gidname.name->cstr ())) res.gid = g->gr_gid; sbp->reply (&res); } void sfsserv::sfs_getcred (svccb *sbp) { u_int32_t authno = sbp->getaui (); if (authno < credtab.size ()) sbp->replyref (credtab[authno]); else sbp->replyref (sfsauth_cred (SFS_NOCRED)); } static ptr sfs_accept (sfsserv_cb cb, int fd) { if (fd < 0) { (*cb) (NULL); return NULL; } tcp_nodelay (fd); ref x = axprt_crypt::alloc (fd); (*cb) (x); return x; } static void sfs_accept_standalone (sfsserv_cb cb, int sfssfd) { sockaddr_in sin; bzero (&sin, sizeof (sin)); socklen_t sinlen = sizeof (sin); int fd = accept (sfssfd, reinterpret_cast (&sin), &sinlen); if (fd >= 0) sfs_accept (cb, fd); else if (errno != EAGAIN) warn ("accept: %m\n"); } void sfssd_slave (sfsserv_cb cb) { if (cloneserv (0, wrap (sfs_accept, cb))) return; warn ("No sfssd detected, running in standalone mode.\n"); int sfssfd = inetsocket (SOCK_STREAM, sfs_port); if (sfssfd < 0) fatal ("binding TCP port %d: %m\n", sfs_port); close_on_exec (sfssfd); listen (sfssfd, 5); fdcb (sfssfd, selread, wrap (sfs_accept_standalone, cb, sfssfd)); }