/*
 * DF-0783 setuid-root helper (extended): discover calling process's ucred
 * address AND dump the full 192-byte ucred to stdout/file.
 *
 * Output format:
 *   UCRED_ADDR 0x<addr>
 *   <192 bytes of raw ucred content>
 *
 * Build: cc -o ucred_helper ucred_helper.c -lkvm
 * Install (root): install -o root -m 4511 ucred_helper /usr/local/sbin/ucred_helper
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/kinfo.h>
#include <kvm.h>

#define P_UCREDOFF 16
#define UCREDSIZE  192

int
main(int argc, char **argv)
{
    kvm_t *kd;
    struct kinfo_proc *kp;
    int nprocs;
    char errbuf[LINE_MAX];
    pid_t target;
    unsigned long proc_addr, ucred_addr;
    unsigned char buf[UCREDSIZE];
    int fd = -1;

    target = getppid();
    if (argc >= 2) target = atoi(argv[1]);
    if (argc >= 3) {
        fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (fd < 0) { perror("open"); return 1; }
    }

    kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
    if (kd == NULL) {
        fprintf(stderr, "ucred_helper: kvm_openfiles: %s\n", errbuf);
        return 1;
    }

    kp = kvm_getprocs(kd, KERN_PROC_PID, target, &nprocs);
    if (kp == NULL || nprocs < 1 || kp[0].kp_paddr == 0) {
        fprintf(stderr, "ucred_helper: kvm_getprocs pid=%d failed\n", target);
        kvm_close(kd);
        return 1;
    }

    proc_addr = (unsigned long)kp[0].kp_paddr;
    if (kvm_read(kd, proc_addr + P_UCREDOFF, &ucred_addr, sizeof(ucred_addr))
        != sizeof(ucred_addr)) {
        fprintf(stderr, "ucred_helper: kvm_read p_ucred: %s\n", kvm_geterr(kd));
        kvm_close(kd);
        return 1;
    }

    if (kvm_read(kd, ucred_addr, buf, UCREDSIZE) != UCREDSIZE) {
        fprintf(stderr, "ucred_helper: kvm_read ucred: %s\n", kvm_geterr(kd));
        kvm_close(kd);
        return 1;
    }

    /* Determine slab zone owning CPU. ZoneSize is 32KB on this guest.
     * zone_header = ucred & ~(32KB-1). z_Cpu is at offset 4 of SLZone. */
    unsigned long zone_hdr = ucred_addr & ~0x7FFFUL;  /* 32KB mask */
    int z_cpu = -1;
    if (kvm_read(kd, zone_hdr + 4, &z_cpu, sizeof(z_cpu)) != sizeof(z_cpu)) {
        /* ignore error */
    }

    fprintf(stderr, "UCRED_ADDR 0x%lx UID %u NGROUPS %u ZONE_CPU %d\n",
            ucred_addr,
            (unsigned)*(unsigned *)(buf + 64),
            (unsigned)*(unsigned short *)(buf + 68),
            z_cpu);
    fprintf(stderr, "PROC_ADDR 0x%lx\n", proc_addr);

    if (fd >= 0) {
        /* File format: 8-byte ucred addr + 192-byte ucred content */
        write(fd, &ucred_addr, sizeof(ucred_addr));
        write(fd, buf, UCREDSIZE);
        close(fd);
    } else {
        /* Stdout: addr line + raw bytes */
        printf("UCRED_ADDR 0x%lx\n", ucred_addr);
        fwrite(buf, 1, UCREDSIZE, stdout);
    }

    kvm_close(kd);
    return 0;
}
