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;
}
Beispiel #2
0
bool
is_safe (const str &d)
{
  if (!d || d[0] == '.' || d[d.len () - 1] == '.' || safe_rxx.search (d))
    return false;
  return true;
}
Beispiel #3
0
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;

}
Beispiel #4
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);
}
Beispiel #11
0
 void identcb (str u, int e) {
   if (u && identrx.search (u))
     user = identrx[2];
   a = NULL;
   cbdone ();
 }
Beispiel #12
0
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;
}