parsav  Check-in [41cbbca855]

Overview
Comment:enable revoking credentials
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 41cbbca855246c9c87507f3e79546b33ee2fe9417e69be83b450c65cfc54c33c
User & Date: lexi on 2021-01-11 02:17:11
Other Links: manifest | tags
Context
2021-01-11
23:33
various cleanups, notices no longer originated from self check-in: 1ba4bbc92f user: lexi tags: trunk
02:17
enable revoking credentials check-in: 41cbbca855 user: lexi tags: trunk
01:53
add auth docs and rsa auth check-in: 0d10a378e9 user: lexi tags: trunk
Changes

Modified backend/pgsql.t from [7d329a757a] to [05f7cb1a74].

   319    319   				$1::bigint,
   320    320   				(select handle from parsav_actors where id = $1::bigint),
   321    321   				'challenge-rsa', $2::bytea,
   322    322   				$3::bigint, $4::text
   323    323   			) on conflict (name,kind,cred) do update set comment = $4::text returning aid
   324    324   		]]
   325    325   	};
          326  +
          327  +	auth_destroy_aid = {
          328  +		params = {uint64}, cmd = true, sql = [[
          329  +			delete from parsav_auth where aid = $1::bigint
          330  +		]];
          331  +	};
          332  +
          333  +	auth_destroy_aid_uid = {
          334  +		params = {uint64,uint64}, cmd = true, sql = [[
          335  +			delete from parsav_auth where aid = $1::bigint and uid = $2::bigint
          336  +		]];
          337  +	};
   326    338   
   327    339   	auth_privs_clear = {
   328    340   		params = {uint64}, cmd = true, sql = [[
   329    341   			update parsav_auth set restrict = array[]::text[] where aid = $1::bigint
   330    342   		]];
   331    343   	};
   332    344   
................................................................................
   357    369   	};
   358    370   
   359    371   	auth_enum_handle = {
   360    372   		params = {rawstring}, sql = [[
   361    373   			select aid, kind, comment, netmask, blacklist from parsav_auth where name = $1::text
   362    374   		]];
   363    375   	};
          376  +
          377  +	auth_fetch_aid = {
          378  +		params = {uint64}, sql = [[
          379  +			select aid, uid, kind, comment, netmask, blacklist from parsav_auth where aid = $1::bigint
          380  +		]];
          381  +	};
   364    382   
   365    383   	post_save = {
   366    384   		params = {
   367    385   			uint64, uint32, int64;
   368    386   			rawstring, rawstring, rawstring;
   369    387   		}, cmd = true, sql = [[
   370    388   			update parsav_posts set
................................................................................
  1636   1654   				var react = r:_string(i,5)
  1637   1655   				lib.str.ncpy(n.reaction, react.ptr, lib.math.smallest(react.ct,[(`n.reaction).tree.type.N]))
  1638   1656   			end
  1639   1657   		end
  1640   1658   
  1641   1659   		return notes
  1642   1660   	end];
         1661  +
         1662  +	auth_fetch_aid = [terra(
         1663  +		src: &lib.store.source,
         1664  +		aid: uint64
         1665  +	): lib.mem.ptr(lib.store.auth)
         1666  +		var r = queries.auth_fetch_aid.exec(src,aid)
         1667  +		if r.sz == 0 then return [lib.mem.ptr(lib.store.auth)].null() end
         1668  +		var kind = r:_string(0, 2)
         1669  +		var comment = r:_string(0, 3)
         1670  +		var a = [ lib.str.encapsulate(lib.store.auth, {
         1671  +			kind = {`kind.ptr, `kind.ct+1};
         1672  +			comment = {`comment.ptr, `comment.ct+1};
         1673  +		}) ]
         1674  +		a.ptr.aid = r:int(uint64, 0, 0)
         1675  +		a.ptr.uid = r:int(uint64, 0, 1)
         1676  +		if r:null(0,3)
         1677  +			then a.ptr.netmask.pv = 0
         1678  +			else a.ptr.netmask = r:cidr(0, 4)
         1679  +		end
         1680  +		a.ptr.blacklist = r:bool(0, 5)
         1681  +		return a
         1682  +	end];
  1643   1683   
  1644   1684   	auth_enum_uid = [terra(
  1645   1685   		src: &lib.store.source,
  1646   1686   		uid: uint64
  1647         -	): lib.mem.ptr(lib.mem.ptr(lib.store.auth))
         1687  +	): lib.mem.lstptr(lib.store.auth)
  1648   1688   		var r = queries.auth_enum_uid.exec(src,uid)
  1649         -		if r.sz == 0 then return [lib.mem.ptr(lib.mem.ptr(lib.store.auth))].null() end
         1689  +		if r.sz == 0 then return [lib.mem.lstptr(lib.store.auth)].null() end
  1650   1690   		var ret = lib.mem.heapa([lib.mem.ptr(lib.store.auth)], r.sz)
  1651   1691   		for i=0, r.sz do
  1652   1692   			var kind = r:_string(i, 1)
  1653   1693   			var comment = r:_string(i, 2)
  1654   1694   			var a = [ lib.str.encapsulate(lib.store.auth, {
  1655   1695   				kind = {`kind.ptr, `kind.ct+1};
  1656   1696   				comment = {`comment.ptr, `comment.ct+1};
................................................................................
  1724   1764   		queries.auth_purge_type.exec(src, handle, uid, 'otp-%')
  1725   1765   	end];
  1726   1766   
  1727   1767   	auth_purge_trust = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
  1728   1768   		queries.auth_purge_type.exec(src, handle, uid, 'trust')
  1729   1769   	end];
  1730   1770   
         1771  +	auth_destroy_aid = [terra(
         1772  +		src: &lib.store.source,
         1773  +		aid: uint64
         1774  +	): {} queries.auth_destroy_aid.exec(src,aid) end];
         1775  +
         1776  +	auth_destroy_aid_uid = [terra(
         1777  +		src: &lib.store.source,
         1778  +		aid: uint64,
         1779  +		uid: uint64
         1780  +	): {} queries.auth_destroy_aid_uid.exec(src,aid,uid) end];
         1781  +
  1731   1782   	artifact_quicksearch = [terra(
  1732   1783   		src: &lib.store.source,
  1733   1784   		hash: binblob
  1734   1785   	): {uint64, bool}
  1735   1786   		var srec = queries.artifact_quicksearch.exec(src, hash)
  1736   1787   		if srec.sz > 0 then
  1737   1788   			defer srec:free()

Modified route.t from [cd7a14ae6e] to [57b2f599e2].

   354    354   	::badop :: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end
   355    355   	::noauth:: do co:complain(401, 'unauthorized', 'you have not supplied the necessary credentials to perform this operation') return end
   356    356   end
   357    357   
   358    358   local terra 
   359    359   credsec_for_uid(co: &lib.srv.convo, uid: uint64)
   360    360   	var act = co:ppostv('act')
   361         -	lib.dbg('showing credentials')
          361  +	if not act then return true end
          362  +	lib.dbg('handling credential action')
   362    363   	if act:cmp( 'invalidate') then
   363    364   		lib.dbg('setting user\'s cookie validation time to now')
   364    365   		co.who.source:auth_sigtime_user_alter(uid, lib.osclock.time(nil))
   365    366   		-- 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
   366    367   		co:installkey('?',co.aid)
   367         -		return
   368         -	elseif act:cmp( 'newcred') then
          368  +		return false
          369  +	elseif act:cmp('revoke') then
          370  +		var s_cred = co:ppostv('cred')
          371  +		if s_cred:ref() then
          372  +			var cred, ok = lib.math.shorthand.parse(s_cred.ptr, s_cred.ct)
          373  +			if ok then
          374  +				co.srv:auth_destroy_aid_uid(cred,co.who.id)
          375  +			end
          376  +		end
          377  +		return true
          378  +	elseif act:cmp('newcred') then
   369    379   		var cmt = co:ppostv('comment')
   370    380   		var pw = co:ppostv('newpw')
   371    381   		var rsapub = co:ppostv('newrsa'):blob()
   372    382   		var aid: uint64 = 0
   373    383   		if pw:ref() then
   374    384   			var cpw = co:ppostv('rptpw')
   375    385   			if not pw:cmp(cpw) then
   376    386   				co:complain(400,'enrollment failure','the passwords you supplied do not match')
   377         -				return
          387  +				return false
   378    388   			end
   379    389   			aid = co.srv:auth_attach_pw(uid, false, pw, cmt)
   380    390   		elseif rsapub:ref() then
   381    391   			var sig = co:ppostv('sig')
   382    392   			var nonce = co:ppostv('nonce')
   383    393   			var s_noncevld = co:ppostv('noncevld')
   384    394   			var noncevld, ok = lib.math.shorthand.parse(s_noncevld.ptr, s_noncevld.ct)
   385    395   			if not ok then
   386    396   				co:complain(403,'try harder next time','you call that cryptanalysis?')
   387         -				return
          397  +				return false
   388    398   			end
   389    399   
   390    400   			var fr = co.srv.pool:frame()
   391    401   			var hmac = lib.crypt.hmacp(&co.srv.pool, lib.crypt.alg.sha256, co.srv.cfg.secret:blob(), nonce)
   392    402   			if not lib.math.truncate64(hmac.ptr, hmac.ct) == noncevld then
   393    403   				co:complain(403,'nice try','what exactly are you trying to accomplish here, buddy')
   394         -				return
          404  +				return false
   395    405   			end
   396    406   
   397    407   			var pkres = lib.crypt.loadpub(rsapub.ptr,rsapub.ct+1) -- needs NUL
   398    408   			if not pkres.ok then
   399    409   				co:complain(400,'invalid key','the key you have supplied is not a valid PEM or DER file')
   400         -				return
          410  +				return false
   401    411   			end
   402    412   			var pk = pkres.val
   403    413   			defer pk:free()
   404    414   
   405    415   			var decoded = co.srv.pool:alloc(uint8,sig.ct)
   406    416   			var decoded_sz: intptr = 0
   407    417   			if lib.b64.mbedtls_base64_decode(decoded.ptr,sig.ct,&decoded_sz,[&uint8](sig.ptr),sig.ct) ~= 0 then
   408    418   				co:complain(400,'invalid signature','the signature you supplied is not encoded in valid base64')
   409         -				return
          419  +				return false
   410    420   			end
   411    421   
   412    422   			var vfy, secl = lib.crypt.verify(&pk, nonce.ptr, nonce.ct, decoded.ptr, decoded_sz)
   413    423   			if not vfy then
   414    424   				co:complain(403,'verification failed','the signature you supplied does not match the required nonce')
   415         -				return
          425  +				return false
   416    426   			end
   417    427   
   418    428   			var dbuf: uint8[lib.crypt.const.maxdersz]
   419    429   			var derkey = lib.crypt.der(true, &pk, &dbuf[0])
   420    430   			aid = co.srv:auth_attach_rsa(co.who.id, false, derkey, cmt)
   421    431   			co.srv.pool:reset(fr)
   422    432   		end
................................................................................
   446    456   				co.srv:auth_privs_set(aid, privs)
   447    457   			end
   448    458   
   449    459   			lib.dbg('setting netmask restrictions')
   450    460   			var nm = co:pgetv('netmask')
   451    461   		end
   452    462   		co:reroute('?')
   453         -		return
          463  +		return false
   454    464   	end
   455    465   	co:complain(400,'bad request','the operation you have requested is not meaningful in this context')
          466  +	return false
   456    467   end
   457    468   
   458    469   terra http.configure(co: &lib.srv.convo, path: hpath, meth: method.t)
   459    470   	var msg = pstring.null()
   460    471   	-- first things first, do priv checks
   461    472   	if path.ct >= 2 then
   462    473   		if not co.who.rights.powers.config() and (
................................................................................
   513    524   				co.ui_hue = co.srv.cfg.ui_hue
   514    525   			end
   515    526   
   516    527   			msg = 'profile changes saved'
   517    528   			--user_refresh = true -- not really necessary here, actually
   518    529   
   519    530   		elseif path(1):cmp('sec') then
   520         -			credsec_for_uid(co, co.who.id)
          531  +			if not credsec_for_uid(co, co.who.id) then return end
   521    532   		elseif path(1):cmp('avi') then
   522    533   			var act = co:ppostv('act')
   523    534   			if act:ref() and act:cmp('clear') then
   524    535   				co.who.avatarid = 0
   525    536   				co.who.source:actor_save(co.who)
   526    537   				msg = 'avatar reset to default'
   527    538   			else goto badop end
................................................................................
   536    547   							goto nopriv
   537    548   						end
   538    549   					else goto badop end
   539    550   					defer usr:free()
   540    551   
   541    552   					if path.ct == 4 then
   542    553   						if path(3):cmp(lib.str.lit 'cred') then
   543         -							credsec_for_uid(co, userid)
          554  +							if not credsec_for_uid(co, userid) then return end 
   544    555   						end
   545    556   					elseif path.ct == 3 then
   546    557   						var purgestr = co:ppostv("purgestr")
   547    558   						var purgekey = co:ppostv("purgekey")
   548    559   						if purgestr:ref() and purgekey:ref() and purgestr(0) ~= 0 then
   549    560   							if purgestr:cmp(purgekey) then -- destroying account! :O
   550    561   								co.srv:actor_purge_uid(userid)

Modified store.t from [53eb63c414] to [bb9260aa8d].

   419    419   	actor_notice_enum: {&m.source, uint64} -> lib.mem.ptr(m.notice)
   420    420   	actor_rel_create: {&m.source, uint16, uint64, uint64} -> {}
   421    421   	actor_rel_destroy: {&m.source, uint16, uint64, uint64} -> {}
   422    422   	actor_rel_calc: {&m.source, uint64, uint64} -> m.relationship
   423    423   
   424    424   	auth_enum_uid:    {&m.source, uint64}    -> lib.mem.lstptr(m.auth)
   425    425   	auth_enum_handle: {&m.source, rawstring} -> lib.mem.lstptr(m.auth)
          426  +	auth_fetch_aid : {&m.source, uint64} -> lib.mem.ptr(m.auth)
   426    427   	auth_attach_pw:  {&m.source, uint64, bool, pstr, pstr} -> uint64
   427    428   	auth_attach_rsa: {&m.source, uint64, bool, lib.mem.ptr(uint8), pstr} -> uint64
   428    429   		-- uid: uint64
   429    430   		-- reset: bool (delete other passwords?)
   430    431   		-- pw: pstring
   431    432   		-- comment: pstring
   432    433   	auth_privs_set: {&m.source, uint64, m.privset} -> {}
          434  +	auth_destroy_aid: {&m.source, uint64} -> {}
          435  +	auth_destroy_aid_uid: {&m.source, uint64, uint64} -> {}
   433    436   	auth_purge_pw: {&m.source, uint64, rawstring} -> {}
   434    437   	auth_purge_otp: {&m.source, uint64, rawstring} -> {}
   435    438   	auth_purge_trust: {&m.source, uint64, rawstring} -> {}
   436    439   	auth_sigtime_user_fetch: {&m.source, uint64} -> m.timepoint
   437    440   		-- authentication tokens and accounts have a property that controls
   438    441   		-- whether auth cookies dated to a certain point are valid. cookies
   439    442   		-- that are generated before the timepoint are considered invalid.