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.