static bool userinfo2str (strbuf &sb, const sfsauth_userinfo *ui) { str audit = single_char_sub (ui->audit, ':', "."); if (!namerx.match (ui->name) || (ui->owner && !namerx.match (*ui->owner)) || !nobadrx.match (ui->privs) || badcharrx.search (ui->pwauth) || badcharrx.search (audit)) return false; sb << ui->name; sb.fmt (":%u:%u:%u:", ui->id, ui->vers, ui->gid); if (ui->owner) sb << *ui->owner; sb << ":"; ptr<sfspub> pk = sfscrypt.alloc (ui->pubkey); if (!pk) return false; pk->export_pubkey (sb); sb << ":" << ui->privs << ":" << ui->pwauth << ":"; str priv = str2wstr (armor64 (ui->privkey.base (), ui->privkey.size())); sb << priv << ":"; sfs_2schnorr_priv::export_keyhalf (ui->srvprivkey, sb); // sb << ":" << ui->refresh << ":" << ui->timeout; sb << ":" << audit; return true; }
bool is_safe (const str &d) { if (!d || d[0] == '.' || d[d.len () - 1] == '.' || safe_rxx.search (d)) return false; return true; }
int main (int argc, const char *argv[]) { if (argc < 2) return -1; str parent, filename; parentpath (parent, filename, str (argv[1])); warn << parent << "\n"; warn << filename << "\n"; return 0; vec<str> out; splitpath (out, argv[1]); str foo; while (out.size () > 0 && (foo = out.pop_front ())) warn << foo << "\n"; return 0; str path (argv[1]); // vec<str> out; static rxx r ("^(.*)/([^/]+)$"); // static rxx r ("^/*([^/]+)(/.*)?$"); // static rxx r ("^s%/[^/]*$%%"); // static rxx pathsplit ("^/*([^/]+)(/.*)?$"); warn << "path: " << path << "\n"; // path = path/r; if (r.search (path)) { if (r.len(1) != -1) warn << r[1] << " -> "; if (r.len(2) != -1) warn << r[2] << "\n"; warn << "r[0]: " << r[0] << "\n"; } warn << split (&out, "/", path) << "\n"; for (unsigned int i=0; i< out.size(); i++) warn << out[i] << "\n"; return 0; }
int agentconn::cagent_fd (bool required) { if (agentfd >= 0) return agentfd; static rxx sockfdre ("^-(\\d+)?$"); if (agentsock && sockfdre.search (agentsock)) { if (sockfdre[1]) agentfd = atoi (sockfdre[1]); else agentfd = 0; if (!isunixsocket (agentfd)) fatal << "fd specified with '-S' not unix domain socket\n"; } else if (agentsock) { agentfd = unixsocket_connect (agentsock); if (agentfd < 0 && required) fatal ("%s: %m\n", agentsock.cstr ()); } else if (ccd (false)) { int32_t res; if (clnt_stat err = ccd ()->scall (AGENT_GETAGENT, NULL, &res)) { if (required) fatal << "sfscd: " << err << "\n"; } else if (res) { if (required) fatal << "connecting to agent via sfscd: " << strerror (res) << "\n"; } else if ((agentfd = sfscdxprt->recvfd ()) < 0) { fatal << "connecting to agent via sfscd: " << "could not get file descriptor\n"; } } else { if (str sock = agent_usersock (true)) agentfd = unixsocket_connect (sock); if (agentfd < 0 && required) fatal << "sfscd not running and no standalone agent socket\n"; } return agentfd; }
bool groupinfo2str (strbuf &sb, const sfsauth_groupinfo *gi) { str audit = single_char_sub (gi->audit, ':', "."); if (!namerx.match (gi->name) || badcharrx.search (audit) || !nobadrx.match (gi->properties)) return false; sb << gi->name; sb.fmt (":%u:%u:", gi->id, gi->vers); if (!printlist (sb, gi->owners, printmember)) return false; sb << ":"; if (!printlist (sb, gi->members, printmember)) return false; // sb << ":" << gi->refresh << ":" << gi->timeout; sb << ":" << gi->properties; sb << ":" << audit; return true; }
str aekey (const sfsauth_dbrec &ae) { switch (ae.type) { case SFSAUTH_USER: return strbuf () << "USER:"******"GROUP:" << ae.groupinfo->name; case SFSAUTH_CACHEENTRY: return strbuf () << "CACHE:" << ae.cacheentry->key; default: { static rxx knrx ("^[^:]*:[^:]*"); str astr = authdbrec2str (&ae); if (!astr) return NULL; if (!knrx.search (astr)) panic << "missing colon: " << astr << "\n"; return knrx[0]; } } }
bool str2groupinfo (sfsauth_groupinfo *gi, str s) { vec<str> gv; if (split (&gv, colon, s, 8, true) != 7) return false; if (!namerx.match (gv[0]) || badcharrx.search (gv[5])) return false; gi->name = gv[0]; if (!convertint (gv[1], &gi->id) || !convertint (gv[2], &gi->vers) || !parselist (&gi->owners, gv[3], parsemember) || !parselist (&gi->members, gv[4], parsemember) // || !convertint (gv[5], &gi->refresh) // || !convertint (gv[6], &gi->timeout) ) return false; gi->properties = gv[5]; gi->audit = gv[6]; // gi->audit = gv[7]; return true; }
static void parseconfig () { str cf = configfile; parseargs pa (cf); bool errors = false; str hostname; rpc_ptr<sfs_hash> hostid; server *s = NULL; release *r = NULL; extension *e = NULL; char *c; int line; vec<str> av; while (pa.getline (&av, &line)) { if (!strcasecmp (av[0], "BindAddr")) { in_addr addr; u_int16_t port = 0; if (av.size () < 2 || av.size () > 3 || !inet_aton (av[1], &addr) || (av.size () == 3 && !convertint (av[2], &port))) { warn << cf << ":" << line << ": usage: BindAddr addr [port]\n"; errors = true; continue; } if (!port) port = sfs_defport ? sfs_defport : SFS_PORT; sockaddr_in *sinp = static_cast<sockaddr_in *> (xmalloc (sizeof (*sinp))); bzero (sinp, sizeof (*sinp)); sinp->sin_family = AF_INET; sinp->sin_port = htons (port); sinp->sin_addr = addr; #ifdef HAVE_SA_LEN sinp->sin_len = sizeof (*sinp); #endif /* HAVE_SA_LEN */ listenaddrs.push_back (reinterpret_cast<sockaddr *> (sinp)); } else if (!strcasecmp (av[0], "Server")) { if (av.size () != 2) { warn << cf << ":" << line << ": usage: Server {hostname|*}[:hostid]\n"; errors = true; continue; } if (strchr (av[1], ':') || ((c = strchr (av[1], '@')) && strchr (c, ','))) { hostid.alloc (); if (!sfs_parsepath (av[1], &hostname, hostid)) { warn << cf << ":" << line << ": bad hostname/hostid\n"; errors = true; continue; } } else { hostid.clear (); if (av[1] == "*") hostname = sfshostname (); else hostname = av[1]; } for (s = serverlist.first; s; s = serverlist.next (s)) if (hostname == s->host && ((hostid && s->hostid && *hostid == *s->hostid) || (!hostid && !s->hostid))) break; if (!s) s = New server (hostname, hostid); r = NULL; e = NULL; } else if (!strcasecmp (av[0], "Release")) { static rxx relrx ("^(\\d+)\\.(\\d\\d?)$"); if (av.size () != 2 || (!relrx.search (av[1]) && av[1] != "*")) { warn << cf << ":" << line << ": usage Release { N.NN | * }\n"; errors = true; r = NULL; continue; } if (!s) { warn << cf << ":" << line << ": Release must follow Server\n"; errors = true; r = NULL; continue; } u_int32_t rel; if (av[1] == "*") rel = 0xffffffff; else rel = strtoi64 (relrx[1]) * 100 + strtoi64 (relrx[2]); r = s->reltab[rel]; if (!r) s->reltab.insert ((r = New release (rel))); for (e = r->extlist.first; r->extlist.next (e); e = r->extlist.next (e)) ; } else if (!strcasecmp (av[0], "Extensions")) { av.pop_front (); e = r->getext (av); } else if (!strcasecmp (av[0], "Service")) { if (!parse_service (av, e, cf << ":" << line)) errors = true; } else if (!strcasecmp (av[0], "HashCost")) { if (av.size () != 2 || !convertint (av[1], &sfs_hashcost)) { warn << cf << ":" << line << ": usage: HashCost <nbits>\n"; errors = true; } else { if (sfs_hashcost > sfs_maxhashcost) sfs_hashcost = sfs_maxhashcost; str s (strbuf ("SFS_HASHCOST=%d", sfs_hashcost)); xputenv (const_cast<char*>(s.cstr())); } } else if (!strcasecmp (av[0], "RevocationDir")) { if (av.size () != 2) { warn << cf << ":" << line << ": usage: RevocationDir <directory>\n"; errors = true; } else { revocationdir = av[1]; } } else { errors = true; warn << cf << ":" << line << ": unknown directive '" << av[0] << "'\n"; } } if (errors) fatal ("parse errors in configuration file\n"); }
void authclnt::sfsauth_update (svccb *sbp) { update_info i; i.cp = NULL; i.ur = NULL; if (sbp->getaui () >= credtab.size () || !(i.cp = &credtab[sbp->getaui ()]) || i.cp->type != SFS_UNIXCRED || !(i.ur = utab[sbp->getaui ()]) || i.ur->authtype == SFS_NOAUTH) { sbp->reject (AUTH_REJECTEDCRED); return; } i.res.set_ok (false); i.kname.set_type (SFSAUTH_DBKEY_NAME); *i.kname.name = i.cp->unixcred->username; dbfile *cdbp; if (!get_user_cursor (&cdbp, NULL, &i.cdbr, i.kname) || i.cp->unixcred->username != i.cdbr.userinfo->name) { *i.res.errmsg = "could not load credential db record"; sbp->replyref (i.res); return; } if (i.cp->unixcred->uid != i.cdbr.userinfo->id) { *i.res.errmsg = "invalid uid"; warn << i.cp->unixcred->username << " authenticated with uid " << i.cp->unixcred->uid << " while DB record has uid " << i.cdbr.userinfo->id << "\n"; warn << "could user " << i.cp->unixcred->username << " have" << " wrong UID in sfs_users file?\n"; sbp->replyref (i.res); return; } i.argp = sbp->Xtmpl getarg<sfsauth2_update_arg> (); if (i.argp->req.type != SFS_UPDATEREQ || (i.argp->req.rec.type != SFSAUTH_USER && i.argp->req.rec.type != SFSAUTH_GROUP)) { *i.res.errmsg = "invalid request"; sbp->replyref (i.res); return; } i.opts = i.argp->req.opts; if (i.argp->req.authid != authid) { *i.res.errmsg = "invalid authid"; sbp->replyref (i.res); return ; } static rxx adminrx ("(\\A|,)admin(\\Z|,)"); i.admin = cdbp->allow_admin && adminrx.search (i.cdbr.userinfo->privs); if (!update_checksig (sbp, i, cdbp)) return; if (i.argp->req.rec.type == SFSAUTH_USER) update_user (sbp, i); else update_group (sbp, i); sbp->replyref (i.res); }
void authclnt::update_group (svccb *sbp, update_info &i) { dbfile *udbp; ptr<authcursor> uac; sfsauth_dbrec udbr; *i.kname.name = i.argp->req.rec.groupinfo->name; bool create = i.argp->req.rec.groupinfo->id == 0 && i.argp->req.rec.groupinfo->vers == 1; bool exists = get_group_cursor (&udbp, &uac, &udbr, i.kname, true, create); if (exists && create && udbr.groupinfo->id > udbp->grprange->id_max) { *i.res.errmsg = strbuf () << "all group IDs in the allowed range (" << udbp->grprange->id_min << "-" << udbp->grprange->id_max << ") are in use"; return; } if (!exists) { if (!create) { *i.res.errmsg = "perhaps record is read-only " "or database doesn't accept group updates"; return; } else { *i.res.errmsg = "no writable databases that accept group updates"; return; } } if (create && udbr.groupinfo->vers != 0) { *i.res.errmsg = strbuf () << "group `" << udbr.groupinfo->name << "'already exists"; return; } if (!i.admin) { if (create) { str gname; if (!(gname = group_prefix (udbr.groupinfo->name, i.cdbr.userinfo->name)) || gname.len () < 1) { *i.res.errmsg = strbuf () << "group name must be of the form `" << i.cdbr.userinfo->name << ".groupname'"; return; } static rxx groupquotarx ("(\\A|,)groupquota=([0-9]+)(\\Z|,)"); if (groupquotarx.search (i.cdbr.userinfo->privs) || udbp->default_groupquota >= 0) { u_int32_t max_groups; u_int32_t cur_groups; if (groupquotarx.success ()) convertint (groupquotarx[2], &max_groups); else max_groups = udbp->default_groupquota; // XXX - open could fail ptr<authcursor> gac = udbp->db->open (udbp->dbflags); cur_groups = gac->count_group_prefix (strbuf () << i.cdbr.userinfo->name << "."); if (cur_groups + 1 > max_groups) { *i.res.errmsg = strbuf () << "group quota exceeded (current=" << cur_groups << "/quota=" << max_groups << ")"; return; } } } else { ptr<sfspub> pk = sfscrypt.alloc (i.cdbr.userinfo->pubkey); str h = armor32 (pk->get_pubkey_hash ()); sfs_groupmembers list; unsigned int n = udbr.groupinfo->owners.size (); for (unsigned int j = 0; j < n; j++) list.push_back (udbr.groupinfo->owners[j]); if (!group_prefix (udbr.groupinfo->name, i.cdbr.userinfo->name) && !is_a_member (i.cdbr.userinfo->name, h, list)) { *i.res.errmsg = "access denied"; return; } } } if (i.argp->req.rec.groupinfo->vers < 1) { *i.res.errmsg = "version number of record must be greater than 0"; return; } if (i.argp->req.rec.groupinfo->vers != udbr.groupinfo->vers + 1) { *i.res.errmsg = "version mismatch"; return; } uac->ae.groupinfo->vers = i.argp->req.rec.groupinfo->vers; strbuf sb; sb << "Last modified " << timestr () << " by " ; if (uid && !*uid) sb << "*superuser*"; else sb << i.cp->unixcred->username; sb << "@" << client_name; uac->ae.groupinfo->audit = sb; // XXX: checking to make sure that the owners/groups are well-formed happens in update process_group_updates (udbr.groupinfo->owners, i.argp->req.rec.groupinfo->owners); process_group_updates (udbr.groupinfo->members, i.argp->req.rec.groupinfo->members); uac->ae.groupinfo->owners = udbr.groupinfo->owners; uac->ae.groupinfo->members = udbr.groupinfo->members; bool chlogok = write_group_changelog (i.argp->req.rec.groupinfo->name, i.argp->req.rec.groupinfo->vers, i.argp->req.rec.groupinfo->members, sb); if (!chlogok) { *i.res.errmsg = "could not write changelog; database unmodified"; return; } if (!uac->update ()) { // XXX: remove changelog entry *i.res.errmsg = "database refused update; see SFS documentation for correct syntax"; return; } i.res.set_ok (true); udbp->mkpub (); uac->find_group_name (udbr.groupinfo->name); if (global_dbcache) update_dbcache (uac); }
void identcb (str u, int e) { if (u && identrx.search (u)) user = identrx[2]; a = NULL; cbdone (); }
void hclient_t::do_read () { int rc = uio.input (fd); if (rc == 0 && !output_stuff) { stats.empties ++; warn ("no content before EOF\n"); cexit (-1); return;; } else if (rc == 0) { // legitimate EOF cexit (0); return; } else if (rc == -1) { if (errno != EAGAIN) { stats.read_errors ++ ; warn ("read error: %m\n"); cexit (-1); } return; } assert (rc > 0); // for sane Web servers, this is easy... if (mode != SEDA) { while (uio.resid ()) { output_stuff = true; uio.output (1); } return; } // // for SEDA, we've had to parse the header and cut off the // connection, since it doesn't support closed connections // as we've asked. // rc = uio.copyout (bp); uio.rembytes (rc); bp += rc; // // reqsz < 0 if we haven't found a field yet in the header // if (reqsz < 0) { if (sz_rxx.search (buf)) { if (!convertint (sz_rxx[1], &reqsz)) { warn ("invalid length: %s\n", sz_rxx[1].cstr ()); cexit (-1); } } } if (!body) body = strstr (buf, "\r\n\r\n"); // // if we've seen the required number of bytes (as given by // the header) then we're ready to close (or rock!) // if (body && (bp - body >= reqsz)) { rc_ignore (write (1, buf, bp - buf)); if (noisy) warn ("ok, closing up!\n"); cexit (0); } }
bool str2userinfo (sfsauth_userinfo *ui, str s) { str name; vec<str> uv; if (split (&uv, colon, s, 12, true) != 11) return false; str2wstr (uv[7]); str2wstr (uv[8]); str fields[13] = { "name", "uid", "version", "gid", "owner", "pubkey", "privs", "srp", "privkey", "srvprivkey", // "refresh", "timeout", "audit" }; if (!namerx.match (uv[0])) { err_report ("<null>", 1, fields[0], uv[0]); return false; } name = uv[0]; for (int i = 1; i < 4; i++) { if (!decrx.match (uv[i])) { err_report (name, i+1, fields[i], uv[i]); return false; } } if (uv[4].len () && !namerx.match (uv[4])) { err_report (name, 5, fields[4], uv[4]); return false; } for (int i = 6; i < 10; i++) { if (badcharrx.search (uv[i])) { err_report (name, i+1, fields[i], uv[i]); return false; } } #if 0 for (int i = 10; i < 12; i++) { if (!decrx.match (uv[i])) { err_report (name, i+1, fields[i], uv[i]); return false; } } #endif str privkey = dearmor64 (uv[8]); if (!privkey) { err_report (name, 9, fields[8], "could not dearmor64"); return false; } str2wstr (privkey); ui->privkey.setsize (privkey.len ()); memcpy (ui->privkey.base (), privkey, ui->privkey.size ()); ui->name = uv[0]; if (!convertint (uv[1], &ui->id) || !convertint (uv[2], &ui->vers) || !convertint (uv[3], &ui->gid) // || !convertint (uv[10], &ui->refresh) // || !convertint (uv[11], &ui->timeout) ) return false; if (uv[4].len ()) *ui->owner.alloc () = uv[4]; else ui->owner.clear (); ptr<sfspub> pk = sfscrypt.alloc (uv[5]); if (!pk) return false; if (!pk->export_pubkey (&ui->pubkey)) { warn << "Cannot load keypair for " << uv[0] << "\n"; return false; } ui->privs = uv[6]; ui->pwauth = uv[7]; if (uv[9] && uv[9].len ()) { if (!sfs_2schnorr_priv::parse_keyhalf (&ui->srvprivkey, uv[9])) { warn << "Cannot load server keyhalf for " << uv[0] << "\n"; return false; } } else { ui->srvprivkey.set_type (SFSAUTH_KEYHALF_NONE); } // ui->audit = uv[12]; ui->audit = uv[10]; return true; }