:- table attacker_control_process/3, se_file_type/2.
:- table se_can_execute/3.
:- table se_domain_can_change/4, dac_execve/5.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% dac rules

member(X, [X|_]) :- !.

member(X, [_|T]) :- member(X, T).

uid_in_group(Uid, Gid) :-
  user_info(_, Uid, Gid).

uid_in_group(Uid, Gid) :-
  user_info(User, Uid, _), 
  group_info(_, Gid, Group),
  member(User, Group).

perm_set(read, 1, _, _) :- !.
perm_set(write, _, 1, _) :- !.
perm_set(execute, _, _, 1).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% dac_can_access_solid is called only when all the parameters are instantiated
dac_can_access_solid(0, _, _, _, _, _, _, _) :- !.

dac_can_access_solid(Uid, _, Uid, _, Mode, uper(R, W, X), _, _) :-
  !, perm_set(Mode, R, W, X).

dac_can_access_solid(_, Gid, _, Gid, Mode, _, gper(R, W, X), _) :-
  !, perm_set(Mode, R, W, X).

dac_can_access_solid(Uid, _, _, Fgid, Mode, _, gper(R, W, X), _) :-
  uid_in_group(Uid, Fgid), !,
  perm_set(Mode, R, W, X).
  
dac_can_access_solid(_, _, _, _, Mode, _, _, oper(R, W, X)) :-
  perm_set(Mode, R, W, X).

dac_can_access(Uid, Gid, Program, Mode) :-
  file_info(path(Program), type(regular), owner(Fuid), group(Fgid), Uper, Gper, Oper, _, _, _, _, _, _),
  dac_can_access_solid(Uid, Gid, Fuid, Fgid, Mode, Uper, Gper, Oper).

dac_can_execute(Uid, Gid, Program) :-
  dac_can_access(Uid, Gid, Program, execute).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% check whether the new state has more privileges
% privilege_enhancing(Uid, Gid, Domain, NewUid, NewGid, NewDomain)
% (1) Uid/Gid changes, and the old Uid/Gid is not zero
% (2) Domain changes

privilege_enhancing(Uid, _, _, NewUid, _, _) :-
  Uid \== 0,
  Uid \== NewUid.

privilege_enhancing(_, Gid, _, _, NewGid, _) :-
  Gid \== 0,
  Gid \== NewGid.

privilege_enhancing(_, _, Domain, _, _, NewDomain) :-
  Domain \== NewDomain.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% update DAC portion of process state for executing a program

dac_execve(OUid, OGid, NUid, NGid, Program) :-
  file_info(path(Program), type(regular), owner(Owner), group(Group), _, _, _, setuid(SU), setgid(SG), _, _, _, _),
  dac_setuid(SU, Owner, OUid, NUid),
  dac_setgid(SG, Group, OGid, NGid).

%% setuid bit not set
dac_setuid(0, _, OUid, OUid).

%% setuid bit set
dac_setuid(1, Owner, _, Owner).

%% setgid bit not set
dac_setgid(0, _, OGid, OGid).

%% setgid bit set
dac_setgid(1, Group, _, Group).
  

% selinux file label type
se_file_type(Program, Type) :-
  file_info(path(Program), _, _, _, _, _, _, _, _, _, _, _, se_type(Type)).

% is there program to cause domain change 
se_domain_can_change(Domain, NewDomain, Type, Program) :-
    se_entrypoint(old_dom(Domain), new_dom(NewDomain), type(Type)),
    se_domain_privilege(domain(Domain), type(NewDomain), class(process), op(transition)),
    se_file_type(Program, Type).

se_can_execute(Domain, Program, Type) :-
    se_file_type(Program, Type),
    se_domain_privilege(domain(Domain), type(Type), class(file), op(execute)).


% What happens when Domain executes Program 
se_execve(Domain, NewDomain, Type) :- 
    se_entrypoint(old_dom(Domain), new_dom(NewDomain), type(Type)),
    se_domain_privilege(domain(Domain), type(NewDomain), class(process), op(transition)),
    !.

se_execve(Domain, Domain, _Type).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% attack rules


%% compromising a network daemon program
attacker_control_process_ps(Uid, Gid, Domain) :-
  process_networking(Pid),
  process_running(Pid, Uid, Gid, _Program, _, _, Domain).

%% 
attacker_control_process_pol(0, 0, Domain) :-
  se_domain_privilege(domain(Domain), _, _, op(listen)).

attacker_control_process(Uid, Gid, Domain) :-
  attacker_control_process_ps(Uid, Gid, Domain).

%% Load a new setuid root program
%% or,
%% Load an ordinary program, considering only those causing domain transitions
attacker_control_process(NewUid, NewGid, NewDomain) :-
  attacker_control_process(Uid, Gid, Domain),
  se_can_execute(Domain, NewProgram, Type),
  dac_can_execute(Uid, Gid, NewProgram),
  se_execve(Domain, NewDomain, Type),
  dac_execve(Uid, Gid, NewUid, NewGid, NewProgram),
  privilege_enhancing(Uid, Gid, Domain, NewUid, NewGid, NewDomain),
  fmt_write("(%d,%d,%s)->(%d,%d,%s) %s\n", args(Uid,Gid,Domain,NewUid,NewGid,NewDomain,NewProgram)).

attacker_succeed_process(goal(load_kernel_module), proc(0, _Gid, Domain)) :-
  se_domain_privilege(domain(Domain), _, class(capability), op(sys_module)).

%attacker_succeed(goal(load_kernel_module), ExploitProgs) :-
%  attacker_control_process(proc(_Program, 0, _Gid, Domain), ExploitProgs),
%  se_domain_privilege(domain(Domain), _, class(capability), op(sys_module)).

output_control_process :- attacker_control_process(Uid,Gid,Domain), write(con(Uid,Gid,Domain)), nl.
output_succeed_process :- attacker_control_process(Uid,Gid,Domain), attacker_succeed_process(_, proc(Uid,Gid,Domain)), write(suc(Uid,Gid,Domain)), nl.

