FPPS4/sys/vfs/sys_capability.pas

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.