mirror of https://github.com/red-prig/fpPS4.git
348 lines
8.2 KiB
Plaintext
348 lines
8.2 KiB
Plaintext
unit sys_capability;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
sys_event,
|
|
vcapability,
|
|
vfile,
|
|
vuio,
|
|
vstat,
|
|
vm;
|
|
|
|
function sys_cap_new():Integer;
|
|
function sys_cap_getrights():Integer;
|
|
function sys_cap_enter():Integer;
|
|
function sys_cap_getmode():Integer;
|
|
|
|
{
|
|
* struct capability describes a capability, and is hung off of its struct
|
|
* file f_data field. cap_file and cap_rightss are static once hooked up, as
|
|
* neither the object it references nor the rights it encapsulates are
|
|
* permitted to change.
|
|
}
|
|
type
|
|
p_capability=^t_capability;
|
|
t_capability=packed record
|
|
cap_object:Pointer; { Underlying object's file. }
|
|
cap_file :Pointer; { Back-pointer to cap's file. }
|
|
cap_rights:cap_rights_t; { Mask of rights on object. }
|
|
end;
|
|
|
|
function cap_check(c:p_capability;rights:cap_rights_t):Integer;
|
|
function cap_rights(fp_cap:p_file):cap_rights_t;
|
|
function cap_funwrap(fp_cap:p_file;rights:cap_rights_t;fpp:pp_file):Integer;
|
|
function cap_funwrap_mmap(fp_cap:p_file;rights:cap_rights_t;maxprotp:PByte;fpp:pp_file):Integer;
|
|
|
|
function kern_capwrap(fp:p_file;rights:cap_rights_t;capfdp:PInteger):Integer;
|
|
|
|
function capability_close(fp:p_file):Integer;
|
|
function capability_read(fp:p_file;uio:p_uio;flags:Integer):Integer;
|
|
function capability_write(fp:p_file;uio:p_uio;flags:Integer):Integer;
|
|
function capability_truncate(fp:p_file;length:Int64):Integer;
|
|
function capability_ioctl(fp:p_file;com:QWORD;data:Pointer):Integer;
|
|
function capability_poll(fp:p_file;events:Integer):Integer;
|
|
function capability_kqfilter(fp:p_file;kn:p_knote):Integer;
|
|
function capability_stat(fp:p_file;sb:p_stat):Integer;
|
|
function capability_chmod(fp:p_file;mode:mode_t):Integer;
|
|
function capability_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
|
|
|
|
const
|
|
capability_ops:fileops=(
|
|
fo_read :@capability_read;
|
|
fo_write :@capability_write;
|
|
fo_truncate:@capability_truncate;
|
|
fo_ioctl :@capability_ioctl;
|
|
fo_poll :@capability_poll;
|
|
fo_kqfilter:@capability_kqfilter;
|
|
fo_stat :@capability_stat;
|
|
fo_close :@capability_close;
|
|
fo_chmod :@capability_chmod;
|
|
fo_chown :@capability_chown;
|
|
fo_flags :DFLAG_PASSABLE
|
|
);
|
|
|
|
capability_ops_unpassable:fileops=(
|
|
fo_read :@capability_read;
|
|
fo_write :@capability_write;
|
|
fo_truncate:@capability_truncate;
|
|
fo_ioctl :@capability_ioctl;
|
|
fo_poll :@capability_poll;
|
|
fo_kqfilter:@capability_kqfilter;
|
|
fo_stat :@capability_stat;
|
|
fo_close :@capability_close;
|
|
fo_chmod :@capability_chmod;
|
|
fo_chown :@capability_chown;
|
|
fo_flags :0
|
|
);
|
|
|
|
implementation
|
|
|
|
uses
|
|
errno,
|
|
kern_descrip;
|
|
|
|
{
|
|
* Stub Capability functions for when options CAPABILITIES isn't compiled
|
|
* into the kernel.
|
|
}
|
|
function sys_cap_new():Integer;
|
|
begin
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function sys_cap_getrights():Integer;
|
|
begin
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function sys_cap_enter():Integer;
|
|
begin
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function sys_cap_getmode():Integer;
|
|
begin
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function cap_check(c:p_capability;rights:cap_rights_t):Integer;
|
|
begin
|
|
if ((c^.cap_rights or rights)<>c^.cap_rights) then
|
|
begin
|
|
Exit(ENOTCAPABLE);
|
|
end;
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* Extract rights from a capability for monitoring purposes -- not for use in
|
|
* any other way, as we want to keep all capability permission evaluation in
|
|
* this one file.
|
|
}
|
|
function cap_rights(fp_cap:p_file):cap_rights_t; public;
|
|
var
|
|
c:p_capability;
|
|
begin
|
|
Assert(fp_cap^.f_type=DTYPE_CAPABILITY,'cap_rights: !capability');
|
|
|
|
c:=fp_cap^.f_data;
|
|
Exit(c^.cap_rights);
|
|
end;
|
|
|
|
function cap_funwrap(fp_cap:p_file;rights:cap_rights_t;fpp:pp_file):Integer; public;
|
|
var
|
|
c:p_capability;
|
|
error:Integer;
|
|
begin
|
|
if (fp_cap=nil) then Exit(EINVAL);
|
|
if (fp_cap^.f_type<>DTYPE_CAPABILITY) then
|
|
begin
|
|
fpp^:=fp_cap;
|
|
Exit(0);
|
|
end;
|
|
c:=fp_cap^.f_data;
|
|
error:=cap_check(c, rights);
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
fpp^:=c^.cap_object;
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* Slightly different routine for memory mapping file descriptors: unwrap the
|
|
* capability and check CAP_MMAP, but also Exita bitmask representing the
|
|
* maximum mapping rights the capability allows on the object.
|
|
}
|
|
function cap_funwrap_mmap(fp_cap:p_file;rights:cap_rights_t;maxprotp:PByte;fpp:pp_file):Integer; public;
|
|
var
|
|
c:p_capability;
|
|
maxprot:Byte;
|
|
error:Integer;
|
|
begin
|
|
if (fp_cap^.f_type<>DTYPE_CAPABILITY) then
|
|
begin
|
|
fpp^:=fp_cap;
|
|
maxprotp^:=VM_PROT_ALL;
|
|
Exit(0);
|
|
end;
|
|
c:=fp_cap^.f_data;
|
|
error:=cap_check(c, rights or CAP_MMAP);
|
|
if (error<>0) then
|
|
Exit(error);
|
|
fpp^:=c^.cap_object;
|
|
maxprot:=0;
|
|
if (c^.cap_rights and CAP_READ)<>0 then
|
|
begin
|
|
maxprot:=maxprot or VM_PROT_READ;
|
|
end;
|
|
if (c^.cap_rights and CAP_WRITE)<>0 then
|
|
begin
|
|
maxprot:=maxprot or VM_PROT_WRITE;
|
|
end;
|
|
if (c^.cap_rights and CAP_MAPEXEC)<>0 then
|
|
begin
|
|
maxprot:=maxprot or VM_PROT_EXECUTE;
|
|
end;
|
|
maxprotp^:=maxprot;
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* Create a capability to wrap around an existing file.
|
|
}
|
|
function kern_capwrap(fp:p_file;rights:cap_rights_t;capfdp:PInteger):Integer;
|
|
var
|
|
cp,cp_old:p_capability;
|
|
fp_object,fcapp:p_file;
|
|
error:Integer;
|
|
begin
|
|
if ((rights or CAP_MASK_VALID)<>CAP_MASK_VALID) then
|
|
begin
|
|
Exit(EINVAL);
|
|
end;
|
|
|
|
{
|
|
* If a new capability is being derived from an existing capability,
|
|
* then the new capability rights must be a subset of the existing
|
|
* rights.
|
|
}
|
|
if (fp^.f_type=DTYPE_CAPABILITY) then
|
|
begin
|
|
cp_old:=fp^.f_data;
|
|
if ((cp_old^.cap_rights or rights)<>cp_old^.cap_rights) then
|
|
begin
|
|
Exit(ENOTCAPABLE);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* Allocate a new file descriptor to hang the capability off of.
|
|
}
|
|
error:=falloc(@fcapp, capfdp, fp^.f_flag);
|
|
if (error<>0) then
|
|
begin
|
|
Exit(error);
|
|
end;
|
|
|
|
{
|
|
* Rather than nesting capabilities, directly reference the object an
|
|
* existing capability references. There's nothing else interesting
|
|
* to preserve for future use, as we've incorporated the previous
|
|
* rights mask into the new one. This prevents us from having to
|
|
* deal with capability chains.
|
|
}
|
|
if (fp^.f_type=DTYPE_CAPABILITY) then
|
|
fp_object:=p_capability(fp^.f_data)^.cap_object
|
|
else
|
|
fp_object:=fp;
|
|
|
|
fhold(fp_object);
|
|
cp:=AllocMem(SizeOf(t_capability));
|
|
cp^.cap_rights:=rights;
|
|
cp^.cap_object:=fp_object;
|
|
cp^.cap_file:=fcapp;
|
|
|
|
if ((fp^.f_flag and DFLAG_PASSABLE)<>0) then
|
|
finit(fcapp, fp^.f_flag, DTYPE_CAPABILITY, cp, @capability_ops)
|
|
else
|
|
finit(fcapp, fp^.f_flag, DTYPE_CAPABILITY, cp, @capability_ops_unpassable);
|
|
|
|
{
|
|
* Release our private reference (the proc filedesc still has one).
|
|
}
|
|
fdrop(fcapp);
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* When a capability is closed, simply drop the reference on the underlying
|
|
* object and free the capability. fdrop() will handle the case where the
|
|
* underlying object also needs to close, and the caller will have already
|
|
* performed any object-specific lock or mqueue handling.
|
|
}
|
|
function capability_close(fp:p_file):Integer;
|
|
var
|
|
c:p_capability;
|
|
fp_object:p_file;
|
|
begin
|
|
Assert(fp^.f_type=DTYPE_CAPABILITY,('capability_close: !capability'));
|
|
|
|
c:=fp^.f_data;
|
|
fp^.f_ops:=@badfileops;
|
|
fp^.f_data:=nil;
|
|
fp_object:=c^.cap_object;
|
|
FreeMem(c);
|
|
|
|
Exit(fdrop(fp_object));
|
|
end;
|
|
|
|
{
|
|
* In general, file descriptor operations should never make it to the
|
|
* capability, only the underlying file descriptor operation vector, so panic
|
|
* if we do turn up here.
|
|
}
|
|
function capability_read(fp:p_file;uio:p_uio;flags:Integer):Integer;
|
|
begin
|
|
Assert(False,'capability_read');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_write(fp:p_file;uio:p_uio;flags:Integer):Integer;
|
|
begin
|
|
Assert(False,'capability_write');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_truncate(fp:p_file;length:Int64):Integer;
|
|
begin
|
|
Assert(False,'capability_truncate');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_ioctl(fp:p_file;com:QWORD;data:Pointer):Integer;
|
|
begin
|
|
Assert(False,'capability_ioctl');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_poll(fp:p_file;events:Integer):Integer;
|
|
begin
|
|
Assert(False,'capability_poll');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_kqfilter(fp:p_file;kn:p_knote):Integer;
|
|
begin
|
|
Assert(False,'capability_kqfilter');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_stat(fp:p_file;sb:p_stat):Integer;
|
|
begin
|
|
Assert(False,'capability_stat');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_chmod(fp:p_file;mode:mode_t):Integer;
|
|
begin
|
|
Assert(False,'capability_chmod');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
function capability_chown(fp:p_file;uid:uid_t;gid:gid_t):Integer;
|
|
begin
|
|
Assert(False,'capability_chown');
|
|
Exit(ENOSYS);
|
|
end;
|
|
|
|
|
|
|
|
end.
|
|
|