/* $Id: authclient.C,v 1.52 2001/08/20 22:19:24 dm Exp $ */ /* * * Copyright (C) 1999 Michael Kaminsky (kaminsky@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 "authserv.h" #include "rxx.h" #if HAVE_GETSPNAM #include #endif /* HAVE_GETSPNAM */ extern "C" char *crypt (const char *, const char *); authclient::authclient (ptr _x, const authunix_parms *aup) : x (_x), authid_valid (false) { if ((unixauth = aup)) uid = aup->aup_uid; authsrv = asrv::alloc (x, sfsauth_program_1, wrap (this, &authclient::dispatch)); sfssrv = asrv::alloc (x, sfs_program_1, wrap (this, &authclient::dispatch)); } void authclient::dologin (svccb *sbp) { sfs_loginarg *larg = sbp->template getarg (); sfsauth_loginres res (SFSLOGIN_BAD); sfs_autharg aarg; if (bytes2xdr (aarg, larg->certificate)) { switch (aarg.type) { case SFS_NOAUTH: res.set_status (SFSLOGIN_OK); bzero (&res.resok->authid, sizeof (res.resok->authid)); res.resok->seqno = larg->seqno; res.resok->cred.set_type (SFS_NOCRED); break; case SFS_AUTHREQ: { // Check the signature of signed_req with usrkey sfs_signed_authreq authreq; rabin_pub usrkey (aarg.req->usrkey); str msg = usrkey.verify_r (aarg.req->signed_req, sizeof (authreq)); authentry ae; ae.pubkey = "0x" << usrkey.n.getstr (16); if (msg && str2xdr (authreq, msg) && authreq.type == SFS_SIGNED_AUTHREQ && authreq.seqno == larg->seqno && authlookup (&ae)) { res.set_status (SFSLOGIN_OK); res.resok->cred.set_type (SFS_UNIXCRED); if (unixlookup (&ae, res.resok->cred.unixcred.addr ())) { res.resok->authid = authreq.authid; res.resok->seqno = authreq.seqno; } else res.set_status (SFSLOGIN_BAD); } break; } default: res.set_status (SFSLOGIN_BAD); break; } } sbp->reply (&res); } void authclient::doregister (svccb *sbp) { sfsauth_registerarg *arg = sbp->template getarg (); str2wstr (arg->msg.password); /* Make sure that we talking over the unix domain socket? */ if (!unixauth) { sbp->replyref (SFSAUTH_NOTSOCK); return; } if (!accepts_reg) { sbp->replyref (SFSAUTH_NOCHANGES); return; } rabin_pub usrkey (arg->msg.pubkey); str rawmsg = xdr2str (arg->msg, true); if (!rawmsg || arg->msg.type != SFS_AUTHREGISTER || !usrkey.verify (rawmsg, arg->sig)) { sbp->replyref (SFSAUTH_BADSIGNATURE); return; } rawmsg = NULL; struct passwd *pe; if ((pe = getpwnam (arg->msg.username)) == NULL) { sbp->replyref (SFSAUTH_BADUSERNAME); return; } /* XXX - In FreeBSD, pw_uid is an int. Go figure. This isn't a * correct fix. Maybe uid should be a u_int32_t and pw_uid cast to * that. */ if (uid && implicit_cast (pe->pw_uid) != uid) { sbp->replyref (SFSAUTH_WRONGUID); return; } if (!pe->pw_uid) { sbp->replyref (SFSAUTH_DENYROOT); return; } if (uid && !validshell (pe->pw_shell)) { sbp->replyref (SFSAUTH_BADSHELL); return; } if (uid) { #if HAVE_GETSPNAM if (struct spwd *spe = getspnam (arg->msg.username)) { if (strcmp (spe->sp_pwdp, crypt (arg->msg.password.cstr (), spe->sp_pwdp))) { sbp->replyref (SFSAUTH_BADPASSWORD); return; } } else #endif /* HAVE_GETSPNAM */ if (strcmp (pe->pw_passwd, crypt (arg->msg.password.cstr (), pe->pw_passwd))) { sbp->replyref (SFSAUTH_BADPASSWORD); return; } } authentry ae; ae.keyname = arg->msg.username; if (authlookup (&ae)) { sbp->replyref (SFSAUTH_USEREXISTS); return; } if (indenyfile (arg->msg.username)) { sbp->replyref (SFSAUTH_REGISTER_DENYFILE); return; } ae.pubkey = "0x" << usrkey.n.getstr (16); ae.keyname = arg->msg.username; ae.privs = arg->msg.username; if (arg->msg.srpinfo) { ae.srpinfo = arg->msg.srpinfo->info; ae.privkey = arg->msg.srpinfo->privkey; } if (!authadd (&ae)) { sbp->replyref (SFSAUTH_FAILED); return; } sbp->replyref (SFSAUTH_OK); return; } void authclient::doupdate (svccb *sbp) { sfsauth_updatearg *arg = sbp->template getarg (); // Check the signature of signed_req with usrkey rabin_pub oldkey (arg->msg.oldkey); rabin_pub newkey (arg->msg.newkey); str rawupdatereq = xdr2str (arg->msg); if (!oldkey.verify (rawupdatereq, arg->osig) || !newkey.verify (rawupdatereq, arg->nsig)) { sbp->replyref (sfsauth_stat (SFSAUTH_BADSIGNATURE)); return; } if (arg->msg.type != SFS_AUTHUPDATE) { sbp->replyref (sfsauth_stat (SFSAUTH_PROTOERR)); return; } if (!authid_valid || arg->msg.authid != authid) { sbp->replyref (sfsauth_stat (SFSAUTH_BADAUTHID)); return; } if (!accepts_changes) { sbp->replyref (sfsauth_stat (SFSAUTH_NOCHANGES)); return; } authentry olde; olde.pubkey = "0x" << oldkey.n.getstr (16); if (!authlookup (&olde)) { sbp->replyref (sfsauth_stat (SFSAUTH_NOTTHERE)); return; } if (indenyfile (olde.keyname)) { sbp->replyref (sfsauth_stat (SFSAUTH_DENYFILE)); return; } authentry newe; newe.pubkey = "0x" << newkey.n.getstr (16); newe.keyname = olde.keyname; /* disallow changing of privs or name */ newe.privs = olde.privs; if (arg->msg.srpinfo) { newe.srpinfo = arg->msg.srpinfo->info; newe.privkey = arg->msg.srpinfo->privkey; } if (!authupdate (&newe)) { sbp->replyref (sfsauth_stat (SFSAUTH_FAILED)); return; } sbp->replyref (sfsauth_stat (SFSAUTH_OK)); } void authclient::srp_getparms (svccb *sbp) { sfsauth_srpparmsres res(SFSAUTH_OK); res.parms->N = srpglobal.N; res.parms->g = srpglobal.g; sbp->replyref (res); } void authclient::srpinit (svccb *sbp) { sfssrp_init_arg *arg = sbp->template getarg (); sfsauth_srpres res (SFSAUTH_OK); authentry e; e.keyname = arg->username; if (authid_valid && authlookup (&e) && srpserv.init (res.msg, &arg->msg, authid, e.keyname, e.srpinfo) == SRP_NEXT) protected_privkey = e.privkey; else res.set_status (SFSAUTH_FAILED); sbp->replyref (res); } void authclient::dosrp (svccb *sbp) { sfssrp_bytes *arg = sbp->template getarg (); sfsauth_srpres res (SFSAUTH_OK); switch (srpserv.next (res.msg, arg)) { case SRP_NEXT: sbp->replyref (res); break; case SRP_LAST: sbp->replyref (res); privkey = protected_privkey; break; default: res.set_status (SFSAUTH_FAILED); sbp->replyref (res); break; } } void authclient::dofetch (svccb *sbp) { sfsauth_fetchres res (SFSAUTH_FAILED); if (privkey) { res.set_status (SFSAUTH_OK); res.resok->privkey = privkey; res.resok->hostid = hostid; } sbp->replyref (res); } void authclient::dispatch (svccb *sbp) { if (!sbp) { delete this; return; } switch (sbp->prog ()) { case SFS_PROGRAM: switch (sbp->proc ()) { case SFSPROC_NULL: sbp->reply (NULL); return; case SFSPROC_CONNECT: { ci = *sbp->template getarg (); rnd.getbytes (charge.target.base (), charge.target.size ()); charge.bitcost = sfs_hashcost; sfs_connectres res (SFS_OK); res.reply->servinfo = servinfo; res.reply->charge = charge; sbp->reply (&res); break; } case SFSPROC_ENCRYPT: if (!unixauth) { sfs_hash sessid; sfs_server_crypt (sbp, authsrvkey, ci, servinfo, &sessid, charge); sfs_get_authid (&authid, SFS_AUTHSERV, servinfo.host.hostname, &hostid, &sessid); authid_valid = true; } else { /* XXX - sfs_server_crypt would panic, as transport not axprt_crypt */ sbp->reject (PROC_UNAVAIL); } break; default: sbp->reject (PROC_UNAVAIL); break; } break; case SFSAUTH_PROGRAM: switch (sbp->proc ()) { case SFSAUTHPROC_NULL: sbp->reply (NULL); break; case SFSAUTHPROC_LOGIN: dologin (sbp); break; case SFSAUTHPROC_REGISTER: doregister (sbp); break; case SFSAUTHPROC_UPDATE: doupdate (sbp); break; case SFSAUTHPROC_SRP_GETPARAMS: srp_getparms (sbp); break; case SFSAUTHPROC_SRP_INIT: srpinit (sbp); break; case SFSAUTHPROC_SRP_MORE: dosrp (sbp); break; case SFSAUTHPROC_FETCH: dofetch (sbp); break; default: sbp->reject (PROC_UNAVAIL); break; } break; default: sbp->reject (PROG_UNAVAIL); break; } }