/* $Id: authdb.C,v 1.20 2002/11/21 18:54:56 max Exp $ */ /* * * Copyright (C) 2001 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 "authdb.h" #include "qhash.h" #include "rxx.h" #include static str aekey (const sfsauth_dbrec &ae) { static rxx knrx ("^[^:]*:[^:]*"); str astr = authdbrec2str (&ae); if (!astr) return NULL; if (!knrx.search (astr)) panic << "missing colon: " << astr << "\n"; return knrx[0]; } #define SEARCH(compare) \ reset (); \ while (next ()) \ if (compare) \ return true; \ return false; bool authcursor::validate () { bool pe = false, ret = true, flag = true; reset (); while (flag) { flag = next (&pe); if (pe) ret = false; } return ret; } bool authcursor::find_user_name (str name) { SEARCH (ae.type == SFSAUTH_USER && ae.userinfo->name == name); } bool authcursor::find_user_pubkey (const sfspub &pk) { SEARCH (ae.type == SFSAUTH_USER && (pk == ae.userinfo->pubkey || pk == ae.userinfo->srvprivkey)); } bool authcursor::find_user_uid (u_int32_t id) { SEARCH (ae.type == SFSAUTH_USER && ae.userinfo->id == id); } bool authcursor::find_group_name (str name) { SEARCH (ae.type == SFSAUTH_GROUP && ae.groupinfo->name == name); } bool authcursor::find_group_gid (u_int32_t id) { SEARCH (ae.type == SFSAUTH_GROUP && ae.groupinfo->id == id); } void authcursor::getgroups (vec *groups, str user) { reset (); while (next ()) if (ae.type == SFSAUTH_GROUP) { u_int n = ae.groupinfo->members.size (); for (u_int i = 0; i < n; i++) { sfs_idname np = ae.groupinfo->members[i]; if (np == user) { groups->push_back (ae.groupinfo->id); break; } } } } struct authcursor_etc_group : public authcursor { static authcursor_etc_group *lastcursor; authcursor_etc_group () {} ~authcursor_etc_group () { if (lastcursor == this) lastcursor = NULL; } bool setae (struct group *gr); void reset () { setgrent (); lastcursor = this; } bool next (bool *pep = NULL) { assert (lastcursor == this); return setae (getgrent ()); } bool find_user_name (str name) { return false; } bool find_user_pubkey (const sfspub &pk) { return false; } bool find_user_uid (u_int32_t uid) { return false; } bool find_group_name (str name) { return setae (getgrnam (name)); } bool find_group_gid (u_int32_t gid) { return setae (getgrgid (gid)); } }; authcursor_etc_group *authcursor_etc_group::lastcursor; bool authcursor_etc_group::setae (struct group *gr) { if (!gr) { ae.set_type (SFSAUTH_ERROR); return false; } ae.set_type (SFSAUTH_GROUP); ae.groupinfo->name = gr->gr_name; ae.groupinfo->id = gr->gr_gid; ae.groupinfo->vers = 0; ae.groupinfo->owners.setsize (0); ae.groupinfo->audit = ""; int i; for (i = 0; gr->gr_mem[i]; i++) ; ae.groupinfo->members.setsize (i); while (i-- > 0) ae.groupinfo->members[i] = gr->gr_mem[i]; return true; } ptr authdb_etc_group::open (bool writable, mode_t perm, bool wait) { if (writable) return NULL; return New refcounted (); } authcursor_file_append::~authcursor_file_append () { str tmppath = path << ".tmp"; if (wfd >= 0) { close (wfd); if (lf->ok ()) unlink (tmppath); } } ptr authcursor_file_append::alloc (str path, mode_t perm, ref lf) { str tmppath = path << ".tmp"; int fd = open (tmppath, O_CREAT|O_WRONLY|O_TRUNC, perm); if (fd < 0) { warn << tmppath << ": " << strerror (errno) << "\n"; return NULL; } return New refcounted (lf, path, fd); } bool authcursor_file_append::update (bool create) { if (wfd < 0) return false; str astr = authdbrec2str (&ae); if (!astr) return false; str k = aekey (ae); if (keys[k]) { warn << "duplicate: " << astr << "\n"; return false; } keys.insert (k); suio_print (&buf, astr); buf.print ("\n", 1); if (buf.resid () >= 8192) buf.output (wfd); return true; } bool authcursor_file_append::commit () { if (wfd < 0) return false; if (buf.resid () && buf.output (wfd) <= 0 || fsync (wfd) < 0) { warn ("authcursor_file_append::commit: %s: %m\n", path.cstr ()); return false; } assert (!buf.resid ()); close (wfd); wfd = -1; str tmppath = path << ".tmp"; if (lf->ok ()) { if (rename (tmppath, path) < 0) { warn ("authcursor_file_append::commit: %s: rename: %m\n", tmppath.cstr ()); unlink (tmppath); return false; } return true; } else { warn ("authcursor_file_append::commit: %s: lost the lock\n", path.cstr ()); unlink (tmppath); return false; } } struct authcursor_file : public authcursor { const str path; ptr lf; mode_t perm; int fd; suio buf; int lineno; bool error; authcursor_file (str p, mode_t pm, ptr l) : path (p), lf (l), perm (pm), fd (-1), error (false) { reset (); } ~authcursor_file (); void reset (); bool next (bool *pep = NULL); bool update (bool create = false); bool commit () { return !error; } }; authcursor_file::~authcursor_file () { close (fd); } void authcursor_file::reset () { if (fd >= 0) close (fd); fd = open (path, O_RDONLY); if (fd < 0) { if (errno != ENOENT) warn ("%s: %m\n", path.cstr ()); } else lseek (fd, 0, SEEK_SET); buf.clear (); lineno = 1; } bool authcursor_file::next (bool *pep) { if (pep) *pep = false; if (fd < 0) return false; bool flush = false; str line; for (;;) { while (!(line = suio_getline (&buf))) { if (buf.resid () > 0x20000) { buf.clear (); flush = true; continue; } int n = buf.input (fd); if (n > 0) continue; if (n < 0) warn << path << ": " << strerror (errno) << "\n"; else if (buf.resid ()) { warn << path << ": " << lineno << ": incomplete last line\n"; if (pep) *pep = true; } return false; } lineno++; if (flush) { warn << path << ": " << lineno-1 << ": line too long\n"; if (pep) *pep = true; } else if (!str2authdbrec (&ae, line)) { warn << path << ": " << lineno-1 << ": syntax error\n"; if (pep) *pep = true; } else return true; } } bool authcursor_file::update (bool create) { if (!lf) { error = true; return false; } ptr c = authcursor_file_append::alloc (path, perm, lf); if (!c) { error = true; return false; } sfsauth_dbrec dbr = ae; str k = aekey (dbr); if (!k) { error = true; return false; } errno = 0; bool found = false; bool empty = true; for (reset (); next ();) { empty = false; str kk = aekey (ae); if (k == kk) { c->ae = dbr; found = true; } else c->ae = ae; if (!c->update ()) { error = true; return false; } } if (!found) { c->ae = dbr; if (!c->update ()) { error = true; return false; } } if ((errno && (!create || !empty)) || !c->commit ()) { warn << path << ": " << strerror (errno) << "\n"; return false; } return true; } ptr authdb_file::open (bool writable, mode_t perm, bool wait) { ptr lf; if (writable && !(lf = lockfile::alloc (path << ".lock", wait))) return NULL; return New refcounted (path, perm, lf); } ptr authdb_file::trunc (mode_t perm, bool wait) { ptr lf = lockfile::alloc (path << ".lock", wait); if (!lf) return NULL; return authcursor_file_append::alloc (path, perm, lf); }