root/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_nfs_acl.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*
 * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
 */

#include <sys/types.h>
#include <sys/errno.h>
#include <sys/tiuser.h>
#include <setjmp.h>
#include <pwd.h>
#include <grp.h>

#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <string.h>
#include "snoop.h"

#include <sys/stat.h>

extern char *get_sum_line();
extern void check_retransmit();
extern char *sum_nfsfh();
extern int sum_nfsstat();
extern int detail_nfsstat();
extern void detail_nfsfh();
extern void detail_fattr();
extern void skip_fattr();
extern char *sum_nfsfh3();
extern int sum_nfsstat3();
extern int detail_nfsstat3();
extern void detail_post_op_attr();
extern void detail_nfsfh3();
extern int sum_nfsstat4();
extern int detail_nfsstat4();

extern jmp_buf xdr_err;

static void aclcall2();
static void aclreply2();
static void aclcall3();
static void aclreply3();
static void aclcall4();
static void aclreply4();
static void detail_access2();
static char *sum_access2();
static void detail_mask();
static void detail_secattr();
static void detail_aclent();
static char *detail_uname();
static char *detail_gname();
static char *detail_perm(ushort_t);
static void interpret_nfs_acl2(int, int, int, int, int, char *, int);
static void interpret_nfs_acl3(int, int, int, int, int, char *, int);
static void interpret_nfs_acl4(int, int, int, int, int, char *, int);

#define ACLPROC2_NULL           ((unsigned long)(0))
#define ACLPROC2_GETACL         ((unsigned long)(1))
#define ACLPROC2_SETACL         ((unsigned long)(2))
#define ACLPROC2_GETATTR        ((unsigned long)(3))
#define ACLPROC2_ACCESS         ((unsigned long)(4))
#define ACLPROC2_GETXATTRDIR    ((unsigned long)(5))

#define ACLPROC3_NULL           ((unsigned long)(0))
#define ACLPROC3_GETACL         ((unsigned long)(1))
#define ACLPROC3_SETACL         ((unsigned long)(2))
#define ACLPROC3_GETXATTRDIR    ((unsigned long)(3))

#define ACLPROC4_NULL           ((unsigned long)(0))
#define ACLPROC4_GETACL         ((unsigned long)(1))
#define ACLPROC4_SETACL         ((unsigned long)(2))

#define NA_USER_OBJ     0x1
#define NA_USER         0x2
#define NA_GROUP_OBJ    0x4
#define NA_GROUP        0x8
#define NA_CLASS_OBJ    0x10
#define NA_OTHER_OBJ    0x20
#define NA_ACL_DEFAULT  0x1000

#define NA_DEF_USER_OBJ         (NA_USER_OBJ | NA_ACL_DEFAULT)
#define NA_DEF_USER             (NA_USER | NA_ACL_DEFAULT)
#define NA_DEF_GROUP_OBJ        (NA_GROUP_OBJ | NA_ACL_DEFAULT)
#define NA_DEF_GROUP            (NA_GROUP | NA_ACL_DEFAULT)
#define NA_DEF_CLASS_OBJ        (NA_CLASS_OBJ | NA_ACL_DEFAULT)
#define NA_DEF_OTHER_OBJ        (NA_OTHER_OBJ | NA_ACL_DEFAULT)

#define NA_ACL          0x1
#define NA_ACLCNT       0x2
#define NA_DFACL        0x4
#define NA_DFACLCNT     0x8

#define ACCESS2_READ    0x0001
#define ACCESS2_LOOKUP  0x0002
#define ACCESS2_MODIFY  0x0004
#define ACCESS2_EXTEND  0x0008
#define ACCESS2_DELETE  0x0010
#define ACCESS2_EXECUTE 0x0020

static char *procnames_short_v2[] = {
        "NULL2",        /*  0 */
        "GETACL2",      /*  1 */
        "SETACL2",      /*  2 */
        "GETATTR2",     /*  3 */
        "ACCESS2",      /*  4 */
        "GETXATTRDIR2", /*  5 */
};
static char *procnames_short_v3[] = {
        "NULL3",        /*  0 */
        "GETACL3",      /*  1 */
        "SETACL3",      /*  2 */
        "GETXATTRDIR3", /*  3 */
};
static char *procnames_short_v4[] = {
        "NULL4",        /*  0 */
        "GETACL4",      /*  1 */
        "SETACL4",      /*  2 */
};

static char *procnames_long_v2[] = {
        "Null procedure",                       /*  0 */
        "Get file access control list",         /*  1 */
        "Set file access control list",         /*  2 */
        "Get file attributes",                  /*  3 */
        "Check access permission",              /*  4 */
        "Get extended attribute directory",     /*  5 */
};
static char *procnames_long_v3[] = {
        "Null procedure",                       /*  0 */
        "Get file access control list",         /*  1 */
        "Set file access control list",         /*  2 */
        "Get extended attribute directory",     /*  3 */
};
static char *procnames_long_v4[] = {
        "Null procedure",                       /*  0 */
        "Get file access control list",         /*  1 */
        "Set file access control list",         /*  2 */
};

#define MAXPROC_V2      5
#define MAXPROC_V3      3
#define MAXPROC_V4      2

/* ARGSUSED */
void
interpret_nfs_acl(flags, type, xid, vers, proc, data, len)
        int flags, type, xid, vers, proc;
        char *data;
        int len;
{

        if (vers == 2) {
                interpret_nfs_acl2(flags, type, xid, vers, proc, data, len);
                return;
        }

        if (vers == 3) {
                interpret_nfs_acl3(flags, type, xid, vers, proc, data, len);
                return;
        }

        if (vers == 4) {
                interpret_nfs_acl4(flags, type, xid, vers, proc, data, len);
                return;
        }
}

static void
interpret_nfs_acl2(int flags, int type, int xid, int vers, int proc,
    char *data, int len)
{
        char *line;
        char buff[2048];
        int off, sz;
        char *fh;
        ulong_t mask;

        if (proc < 0 || proc > MAXPROC_V2)
                return;

        if (flags & F_SUM) {
                line = get_sum_line();

                if (type == CALL) {
                        (void) sprintf(line, "NFS_ACL C %s",
                            procnames_short_v2[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case ACLPROC2_GETACL:
                                fh = sum_nfsfh();
                                mask = getxdr_u_long();
                                (void) sprintf(line, "%s mask=0x%lx", fh, mask);
                                break;
                        case ACLPROC2_SETACL:
                                (void) sprintf(line, sum_nfsfh());
                                break;
                        case ACLPROC2_GETATTR:
                                (void) sprintf(line, sum_nfsfh());
                                break;
                        case ACLPROC2_ACCESS:
                                fh = sum_nfsfh();
                                (void) sprintf(line, "%s (%s)", fh,
                                    sum_access2());
                                break;
                        case ACLPROC2_GETXATTRDIR:
                                fh = sum_nfsfh();
                                (void) sprintf(line, "%s create=%s", fh,
                                    getxdr_bool() ? "true" : "false");
                                break;
                        default:
                                break;
                        }

                        check_retransmit(line, (ulong_t)xid);
                } else {
                        (void) sprintf(line, "NFS_ACL R %s ",
                            procnames_short_v2[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case ACLPROC2_GETACL:
                                (void) sum_nfsstat(line);
                                break;
                        case ACLPROC2_SETACL:
                                (void) sum_nfsstat(line);
                                break;
                        case ACLPROC2_GETATTR:
                                (void) sum_nfsstat(line);
                                break;
                        case ACLPROC2_ACCESS:
                                if (sum_nfsstat(line) == 0) {
                                        skip_fattr();
                                        line += strlen(line);
                                        (void) sprintf(line, " (%s)",
                                            sum_access2());
                                }
                                break;
                        case ACLPROC2_GETXATTRDIR:
                                if (sum_nfsstat(line) == 0) {
                                        line += strlen(line);
                                        (void) sprintf(line, sum_nfsfh());
                                }
                                break;
                        default:
                                break;
                        }
                }
        }

        if (flags & F_DTAIL) {
                show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
                show_space();
                (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
                    proc, procnames_long_v2[proc]);
                if (type == CALL)
                        aclcall2(proc);
                else
                        aclreply2(proc);
                show_trailer();
        }
}

static void
interpret_nfs_acl3(int flags, int type, int xid, int vers, int proc,
    char *data, int len)
{
        char *line;
        char buff[2048];
        int off, sz;
        char *fh;
        ulong_t mask;

        if (proc < 0 || proc > MAXPROC_V3)
                return;

        if (flags & F_SUM) {
                line = get_sum_line();

                if (type == CALL) {
                        (void) sprintf(line, "NFS_ACL C %s",
                            procnames_short_v3[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case ACLPROC3_GETACL:
                                fh = sum_nfsfh3();
                                mask = getxdr_u_long();
                                (void) sprintf(line, "%s mask=0x%lx", fh, mask);
                                break;
                        case ACLPROC3_SETACL:
                                (void) sprintf(line, sum_nfsfh3());
                                break;
                        case ACLPROC3_GETXATTRDIR:
                                fh = sum_nfsfh3();
                                (void) sprintf(line, "%s create=%s", fh,
                                    getxdr_bool() ? "true" : "false");
                                break;
                        default:
                                break;
                        }

                        check_retransmit(line, (ulong_t)xid);
                } else {
                        (void) sprintf(line, "NFS_ACL R %s ",
                            procnames_short_v3[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case ACLPROC3_GETACL:
                                (void) sum_nfsstat3(line);
                                break;
                        case ACLPROC3_SETACL:
                                (void) sum_nfsstat3(line);
                                break;
                        case ACLPROC3_GETXATTRDIR:
                                if (sum_nfsstat3(line) == 0) {
                                        line += strlen(line);
                                        (void) sprintf(line, sum_nfsfh3());
                                }
                                break;
                        default:
                                break;
                        }
                }
        }

        if (flags & F_DTAIL) {
                show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
                show_space();
                (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
                    proc, procnames_long_v3[proc]);
                if (type == CALL)
                        aclcall3(proc);
                else
                        aclreply3(proc);
                show_trailer();
        }
}

static void
interpret_nfs_acl4(int flags, int type, int xid, int vers, int proc,
    char *data, int len)
{
        char *line;
        char buff[2048];
        int off, sz;
        char *fh;
        ulong_t mask;

        if (proc < 0 || proc > MAXPROC_V4)
                return;

        if (flags & F_SUM) {
                line = get_sum_line();

                if (type == CALL) {
                        (void) sprintf(line, "NFS_ACL C %s",
                            procnames_short_v4[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case ACLPROC4_GETACL:
                                fh = sum_nfsfh3();
                                mask = getxdr_u_long();
                                (void) sprintf(line, "%s mask=0x%lx", fh, mask);
                                break;
                        case ACLPROC4_SETACL:
                                (void) sprintf(line, sum_nfsfh3());
                                break;
                        default:
                                break;
                        }

                        check_retransmit(line, (ulong_t)xid);
                } else {
                        (void) sprintf(line, "NFS_ACL R %s ",
                            procnames_short_v4[proc]);
                        line += strlen(line);
                        switch (proc) {
                        case ACLPROC4_GETACL:
                                (void) sum_nfsstat4(line);
                                break;
                        case ACLPROC4_SETACL:
                                (void) sum_nfsstat4(line);
                                break;
                        default:
                                break;
                        }
                }
        }

        if (flags & F_DTAIL) {
                show_header("NFS_ACL:  ", "Sun NFS_ACL", len);
                show_space();
                (void) sprintf(get_line(0, 0), "Proc = %d (%s)",
                    proc, procnames_long_v4[proc]);
                if (type == CALL)
                        aclcall4(proc);
                else
                        aclreply4(proc);
                show_trailer();
        }
}

int
sum_nfsstat4(char *line)
{
        ulong_t status;
        char *p, *nfsstat4_to_name(int);

        status = getxdr_long();
        p = nfsstat4_to_name(status);
        (void) strcpy(line, p);
        return (status);
}

int
detail_nfsstat4()
{
        ulong_t status;
        char buff[64];
        int pos;

        pos = getxdr_pos();
        status = sum_nfsstat4(buff);

        (void) sprintf(get_line(pos, getxdr_pos()), "Status = %d (%s)",
            status, buff);

        return ((int)status);
}

/*
 * Print out version 2 NFS_ACL call packets
 */
static void
aclcall2(proc)
        int proc;
{

        switch (proc) {
        case ACLPROC2_GETACL:
                detail_nfsfh();
                detail_mask();
                break;
        case ACLPROC2_SETACL:
                detail_nfsfh();
                detail_secattr();
                break;
        case ACLPROC2_GETATTR:
                detail_nfsfh();
                break;
        case ACLPROC2_ACCESS:
                detail_nfsfh();
                detail_access2();
                break;
        default:
                break;
        }
}

/*
 * Print out version 2 NFS_ACL reply packets
 */
static void
aclreply2(proc)
        int proc;
{

        switch (proc) {
        case ACLPROC2_GETACL:
                if (detail_nfsstat() == 0) {
                        detail_fattr();
                        detail_secattr();
                }
                break;
        case ACLPROC2_SETACL:
                if (detail_nfsstat() == 0)
                        detail_fattr();
                break;
        case ACLPROC2_GETATTR:
                if (detail_nfsstat() == 0)
                        detail_fattr();
                break;
        case ACLPROC2_ACCESS:
                if (detail_nfsstat() == 0) {
                        detail_fattr();
                        detail_access2();
                }
                break;
        default:
                break;
        }
}

/*
 * Print out version 3 NFS_ACL call packets
 */
static void
aclcall3(proc)
        int proc;
{

        switch (proc) {
        case ACLPROC3_GETACL:
                detail_nfsfh3();
                detail_mask();
                break;
        case ACLPROC3_SETACL:
                detail_nfsfh3();
                detail_secattr();
                break;
        default:
                break;
        }
}

/*
 * Print out version 3 NFS_ACL reply packets
 */
static void
aclreply3(proc)
        int proc;
{

        switch (proc) {
        case ACLPROC3_GETACL:
                if (detail_nfsstat3() == 0) {
                        detail_post_op_attr("");
                        detail_secattr();
                }
                break;
        case ACLPROC3_SETACL:
                if (detail_nfsstat3() == 0)
                        detail_post_op_attr("");
                break;
        default:
                break;
        }
}

/*
 * Print out version 4 NFS_ACL call packets
 */
static void
aclcall4(proc)
        int proc;
{

        switch (proc) {
        case ACLPROC4_GETACL:
                detail_nfsfh3();
                detail_mask();
                break;
        case ACLPROC4_SETACL:
                detail_nfsfh3();
                detail_secattr();
                break;
        default:
                break;
        }
}

/*
 * Print out version 4 NFS_ACL reply packets
 */
static void
aclreply4(proc)
        int proc;
{

        switch (proc) {
        case ACLPROC4_GETACL:
                if (detail_nfsstat4() == 0) {
                        detail_post_op_attr("");
                        detail_secattr();
                }
                break;
        case ACLPROC4_SETACL:
                if (detail_nfsstat4() == 0)
                        detail_post_op_attr("");
                break;
        default:
                break;
        }
}

static void
detail_access2()
{
        uint_t bits;

        bits = showxdr_u_long("Access bits = 0x%08x");
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(bits, ACCESS2_READ, "Read", "(no read)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(bits, ACCESS2_LOOKUP, "Lookup", "(no lookup)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(bits, ACCESS2_MODIFY, "Modify", "(no modify)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(bits, ACCESS2_EXTEND, "Extend", "(no extend)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(bits, ACCESS2_DELETE, "Delete", "(no delete)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(bits, ACCESS2_EXECUTE, "Execute", "(no execute)"));
}

static char *
sum_access2()
{
        int bits;
        static char buff[22];

        bits = getxdr_u_long();
        buff[0] = '\0';

        if (bits & ACCESS2_READ)
                (void) strcat(buff, "read,");
        if (bits & ACCESS2_LOOKUP)
                (void) strcat(buff, "lookup,");
        if (bits & ACCESS2_MODIFY)
                (void) strcat(buff, "modify,");
        if (bits & ACCESS2_EXTEND)
                (void) strcat(buff, "extend,");
        if (bits & ACCESS2_DELETE)
                (void) strcat(buff, "delete,");
        if (bits & ACCESS2_EXECUTE)
                (void) strcat(buff, "execute,");
        if (buff[0] != '\0')
                buff[strlen(buff) - 1] = '\0';

        return (buff);
}

static void
detail_mask()
{
        ulong_t mask;

        mask = showxdr_u_long("Mask = 0x%lx");
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(mask, NA_ACL, "aclent", "(no aclent)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(mask, NA_ACLCNT, "aclcnt", "(no aclcnt)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(mask, NA_DFACL, "dfaclent", "(no dfaclent)"));
        (void) sprintf(get_line(0, 0), "        %s",
            getflag(mask, NA_DFACLCNT, "dfaclcnt", "(no dfaclcnt)"));
}

static void
detail_secattr()
{

        detail_mask();
        showxdr_long("Aclcnt = %d");
        detail_aclent();
        showxdr_long("Dfaclcnt = %d");
        detail_aclent();
}

static void
detail_aclent()
{
        int count;
        int type;
        int id;
        ushort_t perm;

        count = getxdr_long();
        while (count-- > 0) {
                type = getxdr_long();
                id = getxdr_long();
                perm = getxdr_u_short();
                switch (type) {
                case NA_USER:
                        (void) sprintf(get_line(0, 0), "\tuser:%s:%s",
                            detail_uname(id), detail_perm(perm));
                        break;
                case NA_USER_OBJ:
                        (void) sprintf(get_line(0, 0), "\tuser::%s",
                            detail_perm(perm));
                        break;
                case NA_GROUP:
                        (void) sprintf(get_line(0, 0), "\tgroup:%s:%s",
                            detail_gname(id), detail_perm(perm));
                        break;
                case NA_GROUP_OBJ:
                        (void) sprintf(get_line(0, 0), "\tgroup::%s",
                            detail_perm(perm));
                        break;
                case NA_CLASS_OBJ:
                        (void) sprintf(get_line(0, 0), "\tmask:%s",
                            detail_perm(perm));
                        break;
                case NA_OTHER_OBJ:
                        (void) sprintf(get_line(0, 0), "\tother:%s",
                            detail_perm(perm));
                        break;
                case NA_DEF_USER:
                        (void) sprintf(get_line(0, 0), "\tdefault:user:%s:%s",
                            detail_uname(id), detail_perm(perm));
                        break;
                case NA_DEF_USER_OBJ:
                        (void) sprintf(get_line(0, 0), "\tdefault:user::%s",
                            detail_perm(perm));
                        break;
                case NA_DEF_GROUP:
                        (void) sprintf(get_line(0, 0), "\tdefault:group:%s:%s",
                            detail_gname(id), detail_perm(perm));
                        break;
                case NA_DEF_GROUP_OBJ:
                        (void) sprintf(get_line(0, 0), "\tdefault:group::%s",
                            detail_perm(perm));
                        break;
                case NA_DEF_CLASS_OBJ:
                        (void) sprintf(get_line(0, 0), "\tdefault:mask:%s",
                            detail_perm(perm));
                        break;
                case NA_DEF_OTHER_OBJ:
                        (void) sprintf(get_line(0, 0), "\tdefault:other:%s",
                            detail_perm(perm));
                        break;
                default:
                        (void) sprintf(get_line(0, 0), "\tunrecognized entry");
                        break;
                }
        }
}

static char *
detail_uname(uid_t uid)
{
        struct passwd *pwd;
        static char uidp[10];

        pwd = getpwuid(uid);
        if (pwd == NULL) {
                sprintf(uidp, "%d", uid);
                return (uidp);
        }
        return (pwd->pw_name);
}

static char *
detail_gname(gid_t gid)
{
        struct group *grp;
        static char gidp[10];

        grp = getgrgid(gid);
        if (grp == NULL) {
                sprintf(gidp, "%d", gid);
                return (gidp);
        }
        return (grp->gr_name);
}

static char *perms[] = {
        "---",
        "--x",
        "-w-",
        "-wx",
        "r--",
        "r-x",
        "rw-",
        "rwx"
};
static char *
detail_perm(ushort_t perm)
{

        if (perm >= sizeof (perms) / sizeof (perms[0]))
                return ("?");
        return (perms[perm]);
}