The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
/* $Id: ACL.xs,v 1.19 1997/06/23 03:45:20 dougm Exp $ */

#include <dce/rpc.h>
#include <dce/daclif.h>
#include <dce/aclif.h>
#include <dce/sec_acl_encode.h>
#include <dce/secidmap.h>

#include "../DCE_Perl.h"

#define BLESS_ACL_HANDLE \
  sv = sv_newmortal(); \
  sv_setref_pv(sv,package,(void*)handle); \
  XPUSHs(sv); \
  DCESTATUS

#define BLESS_ACL_ENTRY(e) \
{ \
    SV *sv = sv_newmortal(); \
    sv_setref_pv(sv, "DCE::ACL::entry", (void*)e); \
    XPUSHs(sv); \
}

#define BLESS_ACL(acl) \
{ \
    SV *sv = sv_newmortal(); \
    sv_setref_pv(sv, "DCE::ACL", (void*)acl); \
    XPUSHs(sv); \
}

#define IS_FOREIGN_TYPE(t) ((t==sec_acl_e_type_foreign_user)||(t==sec_acl_e_type_foreign_group))

#define SNAG_FIRST_MANAGER(h, mgr, type, mgr_sv) \
if(!SvTRUE(mgr_sv)) { \
    unsigned32 size_used, num_types; \
    uuid_t manager_types[1]; \
    error_status_t status; \
    sec_acl_get_manager_types(h, type, 1, &size_used, &num_types, manager_types, &status); \
    mgr = manager_types[0]; \
    if(mgr_sv != &sv_undef) \
       __bless_uuid(mgr, mgr_sv); \
} \
else { \
    UUIDmagic_sv(manager_type, mgr_sv); \
}
  
typedef sec_acl_handle_t    DCE__ACL__handle;
typedef sec_acl_list_t  *   DCE__ACL__list;
typedef sec_acl_t       *   DCE__ACL;
typedef sec_acl_entry_t *   DCE__ACL__entry;
typedef sec_acl_printstring_t * DCE__ACL__printstring;

static boolean32
acl_entry_compare(sec_acl_entry_t *e1,
		  sec_acl_entry_t *e2,
		  boolean32 permset_filled)
{
    boolean32 entry_equal = FALSE;
    error_status_t status;

    /*
     * Compare ACL entry types
     */
    if (e1->entry_info.entry_type == e2->entry_info.entry_type)
	entry_equal = TRUE;

    /*
     * Compare ACL entry keys if applicable
     */
    if (entry_equal){
        switch (e1->entry_info.entry_type){
	    case sec_acl_e_type_user:
	    case sec_acl_e_type_group:
	    case sec_acl_e_type_foreign_other:
	    case sec_acl_e_type_user_deleg:
	    case sec_acl_e_type_group_deleg:
	    case sec_acl_e_type_for_other_deleg:
		if (! uuid_equal(&e1->entry_info.tagged_union.id.uuid,
		                 &e2->entry_info.tagged_union.id.uuid, 
				 &status))
		    entry_equal = FALSE;
	        break;
	    case sec_acl_e_type_foreign_user:
	    case sec_acl_e_type_foreign_group:
	    case sec_acl_e_type_for_user_deleg:
	    case sec_acl_e_type_for_group_deleg:
	      if (! uuid_equal(&e1->entry_info.tagged_union.foreign_id.id.uuid,
		               &e2->entry_info.tagged_union.foreign_id.id.uuid, 			      &status))
		    entry_equal = FALSE;
	        break;
	    case sec_acl_e_type_extended:
	    /* extended has a key but does not need to be compared */
	        break;
	    /*
	     * These ACL entries have no keys
	     */
	    case sec_acl_e_type_mask_obj:
	    case sec_acl_e_type_user_obj:
	    case sec_acl_e_type_group_obj:
	    case sec_acl_e_type_user_obj_deleg:
	    case sec_acl_e_type_group_obj_deleg:
	    case sec_acl_e_type_other_obj:
	    case sec_acl_e_type_other_obj_deleg:
	    case sec_acl_e_type_unauthenticated:
	    case sec_acl_e_type_any_other:
	    case sec_acl_e_type_any_other_deleg:
	    default:
	        break;
        } 
    } 

    /*
     * Compare ACL permsets 
     */
    if (entry_equal && permset_filled) {
	if (e1->perms != e2->perms) {
	    entry_equal = FALSE;
	}
    }
    return(entry_equal);
}

static void
free_existing_entry_keys(DCE__ACL acl)
{
    int i;
    sec_acl_entry_t *entry_p;

   if ((acl != NULL) && 
	(acl->sec_acl_entries != NULL)) {
	for (i = 0; i < acl->num_entries; i++) {
	    entry_p = &(acl->sec_acl_entries[i]);
	    if (entry_p) {
		switch (entry_p->entry_info.entry_type)  {
		    case sec_acl_e_type_user:
		    case sec_acl_e_type_group:
		    case sec_acl_e_type_foreign_other:
		    case sec_acl_e_type_user_deleg:
		    case sec_acl_e_type_group_deleg:
		    case sec_acl_e_type_for_other_deleg:
		      if (entry_p->entry_info.tagged_union.id.name)
			 free((void *)entry_p->entry_info.tagged_union.id.name);
		       break;
		    case sec_acl_e_type_foreign_user:
		    case sec_acl_e_type_foreign_group:
		    case sec_acl_e_type_for_user_deleg:
		    case sec_acl_e_type_for_group_deleg:
		      if (entry_p->entry_info.tagged_union.foreign_id.id.name)
			 free((void *)entry_p->entry_info.tagged_union.foreign_id.id.name);
	       	      break;
		    case sec_acl_e_type_extended:
		      if (entry_p->entry_info.tagged_union.extended_info)
			 free((void *)entry_p->entry_info.tagged_union.extended_info);
		      break;
		    default:
		      break;
		} 
	    }
	} 
    }

}

MODULE = DCE::ACL		PACKAGE = DCE::ACL::handle	PREFIX = sec_acl_

void
sec_acl_DESTROY(handle)
DCE::ACL::handle handle

   PPCODE:
   {
   error_status_t status; 
   /* some problems here on DEC & Solaris */
#ifdef HPUX
   sec_acl_release_handle(handle, &status);
#endif
   }

void
sec_acl_get_manager_types(handle, sec_acl_type=sec_acl_type_object, size_avail=1)
DCE::ACL::handle handle
sec_acl_type_t  sec_acl_type
unsigned32 size_avail

    PPCODE: 
    {  
    unsigned32 size_used, num_types;
    uuid_t manager_types[size_avail];
    error_status_t status;
    SV *uuid_sv;
    AV *av;
    int i;

    sec_acl_get_manager_types(handle, sec_acl_type, size_avail, 
			      &size_used, &num_types, manager_types, &status);
    CHK_STS(3);
    iniAV;

    for(i=0; i<size_avail; i++) {
      BLESS_UUID(manager_types[i]);
      av_push(av, uuid_sv);
    }

    if(WANTARRAY) {
	XPUSHs_iv(size_used);
	XPUSHs_iv(num_types);
	XPUSHs(newRV((SV*)av));
	DCESTATUS;
    }
    else 
	XPUSHs(newRV((SV*)av));
    }

void
sec_acl_replace(handle, mgr_sv, sec_acl_type, l)
DCE::ACL::handle handle
SV *mgr_sv
sec_acl_type_t  sec_acl_type
DCE::ACL::list l

    PPCODE:
    {
      error_status_t status;
      uuid_t manager_type;

      SNAG_FIRST_MANAGER(handle, manager_type, sec_acl_type, mgr_sv);
      sec_acl_replace(handle, &manager_type, sec_acl_type, l, &status); 
      DCESTATUS;
    }

void
sec_acl_get_printstring(handle, mgr_sv=&sv_undef, printstring_len=32)
DCE::ACL::handle handle
SV *mgr_sv
unsigned32 printstring_len

    PPCODE:
    {
      uuid_t manager_type, manager_type_chain;
      sec_acl_printstring_t manager_info;
      boolean32 tokenize;
      unsigned32 total_num_printstrings, num_printstrings;
      sec_acl_printstring_t printstrings[printstring_len];
      error_status_t status;
      HV *hv, *info_hv;
      AV *av;
      SV *uuid_sv, *info;
      int i;

      SNAG_FIRST_MANAGER(handle, manager_type, sec_acl_type_object, mgr_sv);

      sec_acl_get_printstring(handle, &manager_type, printstring_len,
			  &manager_type_chain, 
			  &manager_info,
			  &tokenize, 
			  &total_num_printstrings,
			  &num_printstrings, 
			  printstrings, 
			  &status);
      CHK_STS(6);

      iniAV;
      for(i=0; i<num_printstrings; i++) {
	iniHV;
	hv_store(hv, "permissions", 11, 
		 newSViv((IV)printstrings[i].permissions), 0);
 	hv_store(hv, "printstring", 11, 
		 newSVpv(printstrings[i].printstring,0), 0);
 	hv_store(hv, "helpstring", 10, 
		 newSVpv(printstrings[i].helpstring,0), 0);

        /* DCE_TIEHASH(&printstrings[i], "DCE::ACL::printstring", hv); */	    
	av_push(av, newRV((SV*)hv));
      }

      if(WANTARRAY) {
	  EXTEND(sp, 6);
	  BLESS_UUID_mortal(manager_type_chain);
	  PUSHs(uuid_sv); /*chain*/
          DCE_TIEHASH(&manager_info, "DCE::ACL::printstring", info_hv);
          PUSHs(newRV((SV*)info_hv)); /*manager_info*/
	  PUSHs_iv(tokenize);
	  PUSHs_iv(total_num_printstrings);
	  PUSHs_iv(num_printstrings);
	  PUSHs(newRV((SV*)av));
	  DCESTATUS;
      }
      else	  
	  XPUSHs(newRV((SV*)av)); 
      }	  

void
sec_acl_test_access(handle, mgr_sv, permset)
DCE::ACL::handle handle
SV *mgr_sv
sec_acl_permset_t permset

   PPCODE:
   {
     uuid_t manager_type;
     error_status_t status;
     boolean32 res;

     SNAG_FIRST_MANAGER(handle, manager_type, sec_acl_type_object, mgr_sv);
     res = sec_acl_test_access(handle, &manager_type, permset, &status);
     XPUSHs_iv(res);
     if(WANTARRAY)
	 DCESTATUS;
   }

void
sec_acl_test_access_on_behalf(handle, uuid, pac, desired_permset)
DCE::ACL::handle handle
SV *uuid
SV *pac
sec_acl_permset_t desired_permset

    PPCODE:
    {
     uuid_t mgr_type;
     sec_id_pac_t subject;
     error_status_t status;
     boolean32 res;
     HV *hv, *id;
     SV **svp;

     subject.pac_type = sec_id_pac_format_v1;
     hv = (HV*)SvRV(pac);
     subject.authenticated = (boolean32)SvIV(*hv_fetch(hv, "authenticated", 13, 0));
     FETCH_SEC_ID(subject.realm, hv, "realm", 5);
     FETCH_SEC_ID(subject.principal, hv, "principal", 9);
     FETCH_SEC_ID(subject.group, hv, "group", 5);
     subject.num_groups = 0;

     UUIDmagic_sv(mgr_type, uuid);
     res = sec_acl_test_access_on_behalf(handle, &mgr_type, &subject, desired_permset, &status); 

     XPUSHs_iv(res);
     if(WANTARRAY)
	 DCESTATUS;
   }

void
sec_acl_get_access(handle, mgr_sv)
DCE::ACL::handle handle
SV *mgr_sv

   PPCODE:
   {
     sec_acl_permset_t permset;
     uuid_t manager_type;
     error_status_t status;
   
     SNAG_FIRST_MANAGER(handle, manager_type, sec_acl_type_object, mgr_sv);
     sec_acl_get_access(handle, &manager_type, &permset, &status);

     XPUSHs_iv(permset);
     DCESTATUS;
   }

void
sec_acl_lookup(handle, mgr_sv, sec_acl_type=sec_acl_type_object) 
DCE::ACL::handle handle
SV      *mgr_sv
sec_acl_type_t  sec_acl_type

    PPCODE:
    {
    sec_acl_list_t *l = 
	(sec_acl_list_t *)safemalloc(sizeof(sec_acl_list_t));  
    uuid_t manager_type;
    error_status_t status, rstatus;
    SV *list = sv_newmortal();

    SNAG_FIRST_MANAGER(handle, manager_type, sec_acl_type_object, mgr_sv);    
    sec_acl_lookup(handle, &manager_type, sec_acl_type, 
		   l, &status);
    CHK_STS(1);
    sv_setref_pv(list, "DCE::ACL::list", (void*)l);
    XPUSHs(list);
    DCESTATUS;
    }

MODULE = DCE::ACL		PACKAGE = DCE::ACL::list

void
DESTROY(l)
DCE::ACL::list l

    CODE:
    safefree((DCE__ACL__list)l);

unsigned32
num_acls(l)
DCE::ACL::list l

   CODE:
   RETVAL = l->num_acls;
    
   OUTPUT:
   RETVAL

DCE::ACL
acls(l, ...)
DCE::ACL::list l

   PPCODE:
   {
   int i;
   if(items > 1) {
       i = (int)SvIV(ST(1));
       BLESS_ACL(l->sec_acls[i]);
   }
   else {
       if(WANTARRAY) {
	   for(i=0; i < l->num_acls; i++)
	       BLESS_ACL(l->sec_acls[i]);
       }
       else {
	   BLESS_ACL(l->sec_acls[0]);
       }   
   }
   }

MODULE = DCE::ACL		PACKAGE = DCE::ACL        PREFIX = dce_acl_obj_

void
dce_acl_obj_init(self, mgr_sv)
SV *self
SV *mgr_sv

    PPCODE:
    {
    uuid_t manager_type;	
    sec_acl_t *acl = (sec_acl_t *)safemalloc(sizeof(sec_acl_t));
    error_status_t status;
    
    UUIDmagic_sv(manager_type, mgr_sv); 

    dce_acl_obj_init(&manager_type, acl, &status);
    BLESS_ACL(acl);
    if(WANTARRAY)
	DCESTATUS;
    }

void
dce_acl_obj_add_any_other_entry(acl, permset)
DCE::ACL acl
sec_acl_permset_t permset

    PPCODE:
    {
    error_status_t status;
    dce_acl_obj_add_any_other_entry(acl, permset, &status);
    DCESTATUS;
    }

MODULE = DCE::ACL		PACKAGE = DCE::ACL        PREFIX = sec_acl_

void
sec_acl_bind(package, entry_name, bind_to_entry=FALSE)
char *package
unsigned char *entry_name
boolean32 bind_to_entry

    PPCODE:
    {
    sec_acl_handle_t  handle;
    error_status_t status;
    SV *sv;
    package = "DCE::ACL::handle"; 

    sec_acl_bind(entry_name, bind_to_entry, &handle, &status);
    CHK_STS(1);
    BLESS_ACL_HANDLE;
    }

unsigned32
num_entries(acl)
DCE::ACL acl

    CODE:
    RETVAL = acl->num_entries;

    OUTPUT:
    RETVAL

SV *
default_realm(acl)
DCE::ACL acl

    CODE:
    {
    HV *hv;	 
    SV *uuid_sv;
    
    iniHV;
    BLESS_UUID(acl->default_realm.uuid); 
    hv_store(hv, "uuid", 4, uuid_sv, 0);
    hv_store(hv, "name", 4, newSVpv(acl->default_realm.name,0), 0); 
    RETVAL = newRV((SV*)hv);
    }

    OUTPUT:
    RETVAL

SV *
manager_type(acl)
DCE::ACL acl

    CODE: 
    {
    SV *uuid_sv;
    BLESS_UUID(acl->sec_acl_manager_type); 
    RETVAL = uuid_sv;
    }

    OUTPUT:
    RETVAL

void
entries(acl, ...)
DCE::ACL acl

    PPCODE:
    {
    int i;
    if(items > 1) {	
	i = (int)SvIV(ST(1));
	BLESS_ACL_ENTRY(&acl->sec_acl_entries[i]);
    }
    else {
	for(i=0; i<acl->num_entries; i++)
	    BLESS_ACL_ENTRY(&acl->sec_acl_entries[i]);
    }
    }

DCE::ACL::entry
new_entry(acl)
SV *acl

    ALIAS:
    DCE::ACL::new = 1
    DCE::ACL::entry::new = 2

    CODE:
    {
    sec_acl_entry_t *e = (sec_acl_entry_t *)NULL;
    e = (sec_acl_entry_t *)safemalloc(sizeof(sec_acl_entry_t));
    /* e->entry_info.tagged_union.id.uuid = NULL; */
    RETVAL = e;
    }

    OUTPUT:
    RETVAL

void
add(acl, e)
DCE::ACL acl
DCE::ACL::entry e

    PPCODE:
    {
    int i;
    sec_acl_entry_t *new_sec_acl_entries;
    boolean32       entry_found = FALSE;
    error_status_t status;

    SET_STATUS(&status, error_status_ok);

    new_sec_acl_entries = (sec_acl_entry_t *)safemalloc(
       (acl->num_entries + 1) * sizeof(sec_acl_entry_t));

    if (new_sec_acl_entries == NULL){
	SET_STATUS(&status, sec_s_no_memory);
	CHK_STS(0);
    }

    for (i = 0; i < acl->num_entries; i++) {
	if (acl_entry_compare(&acl->sec_acl_entries[i], e, FALSE))
	    entry_found = TRUE;
	new_sec_acl_entries[i] =
			acl->sec_acl_entries[i];
    }

    /* check to see if the entry already exists, if so return */
    if (entry_found) {
	croak("ACL entry already exists!");
	CHK_STS(0);
    } 
    
    /*
     * Discard the old, existing entries
     */
    if (acl->num_entries > 0) 
	safefree((void *) acl->sec_acl_entries);

    /* 
     * Add the new entry 
     */
    new_sec_acl_entries[acl->num_entries] = *e;
			
    acl->sec_acl_entries = new_sec_acl_entries;
    acl->num_entries++;
    DCESTATUS;
    }

void
remove(acl, e)
DCE::ACL acl
DCE::ACL::entry e

    PPCODE:
    {
    int i, j;
    boolean32       entry_found = FALSE;
    error_status_t  status;

    SET_STATUS(&status, error_status_ok);
    /* 
     * We have existing ACL, now check for no ACL entries
     */
    if ((acl->num_entries > 0) &&
	(acl->sec_acl_entries == NULL)){
	SET_STATUS(&status, sec_acl_no_acl_found);
	CHK_STS(0);
    }

    /* 
     * If the specified entry is found, remove it 
     */
    for (i = 0; i < acl->num_entries; i++) {
	if (acl_entry_compare(
			     &(acl->sec_acl_entries[i]),
			     e, 
			     FALSE)) {
	    entry_found = TRUE;

            /* Shift any remaining entries down the list */
	    for (j = i; j < acl->num_entries-1; j++)
	       acl->sec_acl_entries[j] = acl->sec_acl_entries[j+1];
	    acl->num_entries--;
	} 
    } 

    if (! entry_found)
	SET_STATUS(&status, sec_acl_object_not_found);
    DCESTATUS;
    }

int
delete(acl)
DCE::ACL acl

    CODE:
    {
    /* 
     * We have existing ACL, now check for no ACL entries
     */
    if ((acl->num_entries > 0) &&
	(acl->sec_acl_entries == NULL)){
	RETVAL = -1;
    }

    /*
     * Perform request to delete entries 
     */
    if (acl->num_entries > 0) {
	free_existing_entry_keys(acl);
	free((void *) acl->sec_acl_entries);
    }
    acl->num_entries = 0;

    RETVAL = 0;
    }
   
    OUTPUT:
    RETVAL

MODULE = DCE::ACL		PACKAGE = DCE::ACL::entry

void
DESTROY(e)
DCE::ACL::entry e
    
    CODE:
    /* free((void *)e); */

unsigned32
perms(e, ...)
DCE::ACL::entry e

    CODE:
    RETVAL = e->perms;

    if(items > 1) 
       e->perms = (unsigned32)SvIV(ST(1));

    OUTPUT:
    RETVAL
    
SV *
entry_info(e, ...)
DCE::ACL::entry e

    CODE:
    {
    HV *hv;	
    SV *uuid_sv;

    iniHV;
    hv_store(hv, "entry_type", 10,
	     newSViv((IV)e->entry_info.entry_type), 0);

    STORE_SEC_ID(e->entry_info.tagged_union.id, hv, "id", 2);
    if(IS_FOREIGN_TYPE(e->entry_info.entry_type)) 
	STORE_FOREIGN_ID(e->entry_info.tagged_union.foreign_id, hv);

    if(items > 1) {
	HV *set_hv, *f_hv;
	SV **svp;
	set_hv = (HV*)SvRV(ST(1));

	svp = hv_fetch(set_hv, "entry_type", 10, 1);
	if(SvOK(*svp)) 
	    e->entry_info.entry_type = (sec_acl_entry_type_t)SvIV(*svp);

	FETCH_SEC_ID(e->entry_info.tagged_union.id, set_hv, "id", 2);
	if(IS_FOREIGN_TYPE(e->entry_info.entry_type)) 
	    FETCH_FOREIGN_ID(e->entry_info.tagged_union.foreign_id, set_hv);
    }
    RETVAL = newRV((SV*)hv);
    }

    OUTPUT:
    RETVAL

boolean32
compare(e1, e2)
DCE::ACL::entry e1
DCE::ACL::entry e2

    CODE: 
    RETVAL = acl_entry_compare(e1,e2,FALSE);
    
    OUTPUT:
    RETVAL	

MODULE = DCE::ACL       PACKAGE = DCE::ACL::printstring

SV *
FETCH(p, key)
DCE::ACL::printstring p
char *key

    CODE:
    /* printf("FETCH '%s'\n", (char *)p->helpstring); */
    if(strEQ(key, "permissions")) 
	RETVAL = newSViv((IV)p->permissions);
    else if(strEQ(key, "printstring"))
        RETVAL = newSVpv(p->printstring, 0);
    else if(strEQ(key, "helpstring"))
        RETVAL = newSVpv(p->helpstring, 0);
    else
        RETVAL = &sv_undef;

    OUTPUT:
    RETVAL