/* nfs client & server acl query tool */


#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/acl.h>
#include <pwd.h>



usage()
{
    printf("Usage: checkfacl {user|group}:{ID|username}:{rwx} file\n");
}

help()
{
    printf("checkfacl -- query access rights present in posix acl\n\n");
    usage();
    printf("This tool is used to verify that a user/group has the expected access rights. \n");
    printf("The main purpose is to confirm that posix acls are consistent on both the NFS\n");
    printf("client and server.\n");

}

struct entry_t
{
    int type; //user or group
    int ID;
    char name[10];
    char permset[4];
    char filename[40];
};

query(entry)
    struct entry_t * entry;
{
    int index=0;

    acl_t ACL = acl_get_file(entry->filename, ACL_TYPE_ACCESS);
    if(ACL==NULL) { 
	fprintf(stderr,"checkfacl: error retrieving acl for %s\n",entry->filename);
	exit(-1);
    }

    acl_entry_t acl_entry;
    acl_tag_t tag;
    acl_permset_t perms;


    if(acl_get_entry(ACL,ACL_FIRST_ENTRY,&acl_entry)!=1) {
	fprintf(stderr,"checkfacl: error reading acl entry\n");
	exit(-1);
    }

    int IDFOUND=0;
    int checkOther=0;
    uid_t * ID;

    do{

        if(acl_get_tag_type(acl_entry,&tag)!=0) {
		fprintf(stderr,"checkfacl: error getting entry tag type\n");
		exit(-1);
        }

	if((tag == ACL_USER && entry->type == 1) || (tag == ACL_GROUP && entry->type==2)) {
		 ID = (uid_t*)acl_get_qualifier(acl_entry);
	}
	else if( tag==ACL_USER_OBJ && entry->type==1) {
		struct stat fstat;
		stat(entry->filename, &fstat);
		ID = &fstat.st_uid;
	}
	else if( tag==ACL_GROUP_OBJ && entry->type==2) {
		struct stat fstat;
		stat(entry->filename, &fstat);
		ID = &fstat.st_gid;
	}
	else if(tag==ACL_OTHER) {
		if(!IDFOUND) printf("ID %s(%d) not present in acl for file %s\n\n",
					entry->name,entry->ID, entry->filename);
                checkOther=1;
                
        }
	else continue;
 
	if(entry->ID == *ID || checkOther==1) {
		if(!checkOther) IDFOUND=1;
		int error = acl_get_permset(acl_entry,&perms);
		if(error!=0) { printf("acl_get_permset failure\n"); exit(-1); }
		while(entry->permset[index] != '\0'){
			char c = entry->permset[index++];
			switch(c) {
				case 'r': if(acl_get_perm(perms,ACL_READ)) {
						if(checkOther) 
							printf("OTHER perms grant %s(%d) READ access\n",
								entry->name, entry->ID); 
						else
							printf("%s(%d) has READ access\n",
								entry->name, entry->ID); 
					  }
					  else printf("%s(%d) DOES NOT have READ access\n",
								entry->name, entry->ID);
					  break;
				case 'w': if(acl_get_perm(perms,ACL_WRITE)) {
						if(checkOther) 
							printf("OTHER perms grant %s(%d) WRITE access\n",
								entry->name, entry->ID); 
						else
							printf("%s(%d) has WRITE access\n",
								entry->name, entry->ID);
					  }	
					  else printf("%s(%d) DOES NOT have WRITE access\n",
								entry->name, entry->ID);
					  break;
				case 'x': if(acl_get_perm(perms,ACL_EXECUTE)) {
						if(checkOther) 
							printf("OTHER perms grant %s(%d) EXECUTE access\n",
								entry->name, entry->ID); 
						else
							printf("%s(%d) has EXECUTE access\n",
								entry->name, entry->ID);
					  }
					  else printf("%s(%d) DOES NOT have EXECUTE access\n",
								entry->name, entry->ID);
					  break;
				default:  printf("checkfacl: invalid permission entry\n"); 
					  return -1;
					  break;
			}
		}
                break;
	 }
    }while(acl_get_entry(ACL,ACL_NEXT_ENTRY,&acl_entry)==1);

    acl_free(ACL);

    return 0;
}



ParseCommand(argc, argv,entry)
     int argc;
     char **argv;
     struct entry_t * entry;
{

    entry->filename[0]='\0';
    entry->permset[0]='\0';
    char type[5];
    char ID[10];

    if(strcmp(argv[1],"-help")) sscanf(argv[1],"%[^0123456789:]%*c%[^:]%*c%s",type,ID,&entry->permset);   
    else {
	help();
	exit(1);
    }

    if(type[0]=='u') { entry->type=1; }
    else if(type[0]=='g') entry->type=2;
    else {
	printf("checkfacl: invalid entry type\n"); 
	return -1;
    }

    struct passwd * st;

    if(ID[0] > 47 && ID[0]< 58) { /*ID is in int form*/
		sscanf(ID,"%d",&entry->ID); 
		st = getpwuid(entry->ID);
                sscanf(st->pw_name,"%s",entry->name);
    }
    else { /*user name given*/
         sscanf(ID,"%s",entry->name);
	 st = getpwnam(ID);
	 if(st) entry->ID = st->pw_uid;
         else {
		printf("checkfacl: Invalid username\n");
		return -1;
	 }
    }

   // printf("%s:%d:%s\n",type,entry->ID,entry->permset);  //for debugging only!

    //printf("entry->type = %d\n",entry->type);
    

    if(strcmp(argv[2],"-help")) sscanf(argv[2],"%s",entry->filename);
    else {
	help();
        exit(1);
    }
	
    return 0;
}


int
main(argc, argv)
     int argc;
     char **argv;
{
   /*MENU
    *checkfacl -acl aclquery -file filename -help print help menu
    *checkfacl u:user:perm filename
    *checkfacl g:group:perm filename
    *checkfacl -help
    */

    int error=0;

    if(argc==1)  goto usage;
    else if(argc == 2) {
	if(strcmp(argv[1],"-help")) goto usage;
	else help();
        goto end;
    }

    struct entry_t entry;

    error = ParseCommand(argc,argv,&entry);
    
    if(error!=0) goto usage;

    error = query(&entry);

    if(error!=0) goto usage;

    goto end;

usage:
    usage();

end:
    return 0;
}
