Index: backend/pgsql.t ================================================================== --- backend/pgsql.t +++ backend/pgsql.t @@ -321,10 +321,22 @@ 'challenge-rsa', $2::bytea, $3::bigint, $4::text ) on conflict (name,kind,cred) do update set comment = $4::text returning aid ]] }; + + auth_destroy_aid = { + params = {uint64}, cmd = true, sql = [[ + delete from parsav_auth where aid = $1::bigint + ]]; + }; + + auth_destroy_aid_uid = { + params = {uint64,uint64}, cmd = true, sql = [[ + delete from parsav_auth where aid = $1::bigint and uid = $2::bigint + ]]; + }; auth_privs_clear = { params = {uint64}, cmd = true, sql = [[ update parsav_auth set restrict = array[]::text[] where aid = $1::bigint ]]; @@ -359,10 +371,16 @@ auth_enum_handle = { params = {rawstring}, sql = [[ select aid, kind, comment, netmask, blacklist from parsav_auth where name = $1::text ]]; }; + + auth_fetch_aid = { + params = {uint64}, sql = [[ + select aid, uid, kind, comment, netmask, blacklist from parsav_auth where aid = $1::bigint + ]]; + }; post_save = { params = { uint64, uint32, int64; rawstring, rawstring, rawstring; @@ -1638,17 +1656,39 @@ end end return notes end]; + + auth_fetch_aid = [terra( + src: &lib.store.source, + aid: uint64 + ): lib.mem.ptr(lib.store.auth) + var r = queries.auth_fetch_aid.exec(src,aid) + if r.sz == 0 then return [lib.mem.ptr(lib.store.auth)].null() end + var kind = r:_string(0, 2) + var comment = r:_string(0, 3) + var a = [ lib.str.encapsulate(lib.store.auth, { + kind = {`kind.ptr, `kind.ct+1}; + comment = {`comment.ptr, `comment.ct+1}; + }) ] + a.ptr.aid = r:int(uint64, 0, 0) + a.ptr.uid = r:int(uint64, 0, 1) + if r:null(0,3) + then a.ptr.netmask.pv = 0 + else a.ptr.netmask = r:cidr(0, 4) + end + a.ptr.blacklist = r:bool(0, 5) + return a + end]; auth_enum_uid = [terra( src: &lib.store.source, uid: uint64 - ): lib.mem.ptr(lib.mem.ptr(lib.store.auth)) + ): lib.mem.lstptr(lib.store.auth) var r = queries.auth_enum_uid.exec(src,uid) - if r.sz == 0 then return [lib.mem.ptr(lib.mem.ptr(lib.store.auth))].null() end + if r.sz == 0 then return [lib.mem.lstptr(lib.store.auth)].null() end var ret = lib.mem.heapa([lib.mem.ptr(lib.store.auth)], r.sz) for i=0, r.sz do var kind = r:_string(i, 1) var comment = r:_string(i, 2) var a = [ lib.str.encapsulate(lib.store.auth, { @@ -1726,10 +1766,21 @@ auth_purge_trust = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {} queries.auth_purge_type.exec(src, handle, uid, 'trust') end]; + auth_destroy_aid = [terra( + src: &lib.store.source, + aid: uint64 + ): {} queries.auth_destroy_aid.exec(src,aid) end]; + + auth_destroy_aid_uid = [terra( + src: &lib.store.source, + aid: uint64, + uid: uint64 + ): {} queries.auth_destroy_aid_uid.exec(src,aid,uid) end]; + artifact_quicksearch = [terra( src: &lib.store.source, hash: binblob ): {uint64, bool} var srec = queries.artifact_quicksearch.exec(src, hash) Index: route.t ================================================================== --- route.t +++ route.t @@ -356,65 +356,75 @@ end local terra credsec_for_uid(co: &lib.srv.convo, uid: uint64) var act = co:ppostv('act') - lib.dbg('showing credentials') + if not act then return true end + lib.dbg('handling credential action') if act:cmp( 'invalidate') then lib.dbg('setting user\'s cookie validation time to now') co.who.source:auth_sigtime_user_alter(uid, lib.osclock.time(nil)) -- the current session has been invalidated as well, so we need to immediately install a new authentication cookie with the same aid so the user doesn't need to log back in all over again co:installkey('?',co.aid) - return - elseif act:cmp( 'newcred') then + return false + elseif act:cmp('revoke') then + var s_cred = co:ppostv('cred') + if s_cred:ref() then + var cred, ok = lib.math.shorthand.parse(s_cred.ptr, s_cred.ct) + if ok then + co.srv:auth_destroy_aid_uid(cred,co.who.id) + end + end + return true + elseif act:cmp('newcred') then var cmt = co:ppostv('comment') var pw = co:ppostv('newpw') var rsapub = co:ppostv('newrsa'):blob() var aid: uint64 = 0 if pw:ref() then var cpw = co:ppostv('rptpw') if not pw:cmp(cpw) then co:complain(400,'enrollment failure','the passwords you supplied do not match') - return + return false end aid = co.srv:auth_attach_pw(uid, false, pw, cmt) elseif rsapub:ref() then var sig = co:ppostv('sig') var nonce = co:ppostv('nonce') var s_noncevld = co:ppostv('noncevld') var noncevld, ok = lib.math.shorthand.parse(s_noncevld.ptr, s_noncevld.ct) if not ok then co:complain(403,'try harder next time','you call that cryptanalysis?') - return + return false end var fr = co.srv.pool:frame() var hmac = lib.crypt.hmacp(&co.srv.pool, lib.crypt.alg.sha256, co.srv.cfg.secret:blob(), nonce) if not lib.math.truncate64(hmac.ptr, hmac.ct) == noncevld then co:complain(403,'nice try','what exactly are you trying to accomplish here, buddy') - return + return false end var pkres = lib.crypt.loadpub(rsapub.ptr,rsapub.ct+1) -- needs NUL if not pkres.ok then co:complain(400,'invalid key','the key you have supplied is not a valid PEM or DER file') - return + return false end var pk = pkres.val defer pk:free() var decoded = co.srv.pool:alloc(uint8,sig.ct) var decoded_sz: intptr = 0 if lib.b64.mbedtls_base64_decode(decoded.ptr,sig.ct,&decoded_sz,[&uint8](sig.ptr),sig.ct) ~= 0 then co:complain(400,'invalid signature','the signature you supplied is not encoded in valid base64') - return + return false end var vfy, secl = lib.crypt.verify(&pk, nonce.ptr, nonce.ct, decoded.ptr, decoded_sz) if not vfy then co:complain(403,'verification failed','the signature you supplied does not match the required nonce') - return + return false end var dbuf: uint8[lib.crypt.const.maxdersz] var derkey = lib.crypt.der(true, &pk, &dbuf[0]) aid = co.srv:auth_attach_rsa(co.who.id, false, derkey, cmt) @@ -448,13 +458,14 @@ lib.dbg('setting netmask restrictions') var nm = co:pgetv('netmask') end co:reroute('?') - return + return false end co:complain(400,'bad request','the operation you have requested is not meaningful in this context') + return false end terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t) var msg = pstring.null() -- first things first, do priv checks @@ -515,11 +526,11 @@ msg = 'profile changes saved' --user_refresh = true -- not really necessary here, actually elseif path(1):cmp('sec') then - credsec_for_uid(co, co.who.id) + if not credsec_for_uid(co, co.who.id) then return end elseif path(1):cmp('avi') then var act = co:ppostv('act') if act:ref() and act:cmp('clear') then co.who.avatarid = 0 co.who.source:actor_save(co.who) @@ -538,11 +549,11 @@ else goto badop end defer usr:free() if path.ct == 4 then if path(3):cmp(lib.str.lit 'cred') then - credsec_for_uid(co, userid) + if not credsec_for_uid(co, userid) then return end end elseif path.ct == 3 then var purgestr = co:ppostv("purgestr") var purgekey = co:ppostv("purgekey") if purgestr:ref() and purgekey:ref() and purgestr(0) ~= 0 then Index: store.t ================================================================== --- store.t +++ store.t @@ -421,17 +421,20 @@ actor_rel_destroy: {&m.source, uint16, uint64, uint64} -> {} actor_rel_calc: {&m.source, uint64, uint64} -> m.relationship auth_enum_uid: {&m.source, uint64} -> lib.mem.lstptr(m.auth) auth_enum_handle: {&m.source, rawstring} -> lib.mem.lstptr(m.auth) + auth_fetch_aid : {&m.source, uint64} -> lib.mem.ptr(m.auth) auth_attach_pw: {&m.source, uint64, bool, pstr, pstr} -> uint64 auth_attach_rsa: {&m.source, uint64, bool, lib.mem.ptr(uint8), pstr} -> uint64 -- uid: uint64 -- reset: bool (delete other passwords?) -- pw: pstring -- comment: pstring auth_privs_set: {&m.source, uint64, m.privset} -> {} + auth_destroy_aid: {&m.source, uint64} -> {} + auth_destroy_aid_uid: {&m.source, uint64, uint64} -> {} auth_purge_pw: {&m.source, uint64, rawstring} -> {} auth_purge_otp: {&m.source, uint64, rawstring} -> {} auth_purge_trust: {&m.source, uint64, rawstring} -> {} auth_sigtime_user_fetch: {&m.source, uint64} -> m.timepoint -- authentication tokens and accounts have a property that controls