Index: stable/10/contrib/tcpdump/tcpdump.c =================================================================== --- stable/10/contrib/tcpdump/tcpdump.c (revision 280249) +++ stable/10/contrib/tcpdump/tcpdump.c (revision 280250) @@ -1,2288 +1,2288 @@ /* * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: (1) source code distributions * retain the above copyright notice and this paragraph in its entirety, (2) * distributions including binary code include the above copyright notice and * this paragraph in its entirety in the documentation or other materials * provided with the distribution, and (3) all advertising materials mentioning * features or use of this software display the following acknowledgement: * ``This product includes software developed by the University of California, * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of * the University nor the names of its contributors may be used to endorse * or promote products derived from this software without specific prior * written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Support for splitting captures into multiple files with a maximum * file size: * * Copyright (c) 2001 * Seth Webster */ #ifndef lint static const char copyright[] _U_ = "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\ The Regents of the University of California. All rights reserved.\n"; static const char rcsid[] _U_ = "@(#) $Header: /tcpdump/master/tcpdump/tcpdump.c,v 1.283 2008-09-25 21:45:50 guy Exp $ (LBL)"; #endif /* $FreeBSD$ */ /* * tcpdump - monitor tcp/ip traffic on an ethernet. * * First written in 1987 by Van Jacobson, Lawrence Berkeley Laboratory. * Mercilessly hacked and occasionally improved since then via the * combined efforts of Van, Steve McCanne and Craig Leres of LBL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef WIN32 #include "getopt.h" #include "w32_fzs.h" extern int strcasecmp (const char *__s1, const char *__s2); extern int SIZE_BUF; #define off_t long #define uint UINT #endif /* WIN32 */ #ifdef HAVE_SMI_H #include #endif #include #include #include #include #include #include #ifdef __FreeBSD__ -#include +#include #include #include #include #include #include #include #endif /* __FreeBSD__ */ #ifndef WIN32 #include #include #include #include #include #endif /* WIN32 */ /* capabilities convinience library */ #ifdef HAVE_CAP_NG_H #include #endif /* HAVE_CAP_NG_H */ #include "netdissect.h" #include "interface.h" #include "addrtoname.h" #include "machdep.h" #include "setsignal.h" #include "gmt2local.h" #include "pcap-missing.h" #ifndef PATH_MAX #define PATH_MAX 1024 #endif #ifdef SIGINFO #define SIGNAL_REQ_INFO SIGINFO #elif SIGUSR1 #define SIGNAL_REQ_INFO SIGUSR1 #endif netdissect_options Gndo; netdissect_options *gndo = &Gndo; static int dflag; /* print filter code */ static int Lflag; /* list available data link types and exit */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE static int Jflag; /* list available time stamp types */ #endif static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */ static int infodelay; static int infoprint; char *program_name; int32_t thiszone; /* seconds offset from gmt to local time */ /* Forwards */ static RETSIGTYPE cleanup(int); static RETSIGTYPE child_cleanup(int); static void usage(void) __attribute__((noreturn)); static void show_dlts_and_exit(const char *device, pcap_t *pd) __attribute__((noreturn)); static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void ndo_default_print(netdissect_options *, const u_char *, u_int); static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *); static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *); static void droproot(const char *, const char *); static void ndo_error(netdissect_options *ndo, const char *fmt, ...) __attribute__ ((noreturn, format (printf, 2, 3))); static void ndo_warning(netdissect_options *ndo, const char *fmt, ...); #ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int); #endif #if defined(USE_WIN32_MM_TIMER) #include static UINT timer_id; static void CALLBACK verbose_stats_dump(UINT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR); #elif defined(HAVE_ALARM) static void verbose_stats_dump(int sig); #endif static void info(int); static u_int packets_captured; struct printer { if_printer f; int type; }; struct ndo_printer { if_ndo_printer f; int type; }; static struct printer printers[] = { { arcnet_if_print, DLT_ARCNET }, #ifdef DLT_ARCNET_LINUX { arcnet_linux_if_print, DLT_ARCNET_LINUX }, #endif { token_if_print, DLT_IEEE802 }, #ifdef DLT_LANE8023 { lane_if_print, DLT_LANE8023 }, #endif #ifdef DLT_CIP { cip_if_print, DLT_CIP }, #endif #ifdef DLT_ATM_CLIP { cip_if_print, DLT_ATM_CLIP }, #endif { sl_if_print, DLT_SLIP }, #ifdef DLT_SLIP_BSDOS { sl_bsdos_if_print, DLT_SLIP_BSDOS }, #endif { ppp_if_print, DLT_PPP }, #ifdef DLT_PPP_WITHDIRECTION { ppp_if_print, DLT_PPP_WITHDIRECTION }, #endif #ifdef DLT_PPP_BSDOS { ppp_bsdos_if_print, DLT_PPP_BSDOS }, #endif { fddi_if_print, DLT_FDDI }, { null_if_print, DLT_NULL }, #ifdef DLT_LOOP { null_if_print, DLT_LOOP }, #endif { raw_if_print, DLT_RAW }, { atm_if_print, DLT_ATM_RFC1483 }, #ifdef DLT_C_HDLC { chdlc_if_print, DLT_C_HDLC }, #endif #ifdef DLT_HDLC { chdlc_if_print, DLT_HDLC }, #endif #ifdef DLT_PPP_SERIAL { ppp_hdlc_if_print, DLT_PPP_SERIAL }, #endif #ifdef DLT_PPP_ETHER { pppoe_if_print, DLT_PPP_ETHER }, #endif #ifdef DLT_LINUX_SLL { sll_if_print, DLT_LINUX_SLL }, #endif #ifdef DLT_IEEE802_11 { ieee802_11_if_print, DLT_IEEE802_11}, #endif #ifdef DLT_LTALK { ltalk_if_print, DLT_LTALK }, #endif #if defined(DLT_PFLOG) && defined(HAVE_NET_PFVAR_H) { pflog_if_print, DLT_PFLOG }, #endif #ifdef DLT_FR { fr_if_print, DLT_FR }, #endif #ifdef DLT_FRELAY { fr_if_print, DLT_FRELAY }, #endif #ifdef DLT_SUNATM { sunatm_if_print, DLT_SUNATM }, #endif #ifdef DLT_IP_OVER_FC { ipfc_if_print, DLT_IP_OVER_FC }, #endif #ifdef DLT_PRISM_HEADER { prism_if_print, DLT_PRISM_HEADER }, #endif #ifdef DLT_IEEE802_11_RADIO { ieee802_11_radio_if_print, DLT_IEEE802_11_RADIO }, #endif #ifdef DLT_ENC { enc_if_print, DLT_ENC }, #endif #ifdef DLT_SYMANTEC_FIREWALL { symantec_if_print, DLT_SYMANTEC_FIREWALL }, #endif #ifdef DLT_APPLE_IP_OVER_IEEE1394 { ap1394_if_print, DLT_APPLE_IP_OVER_IEEE1394 }, #endif #ifdef DLT_IEEE802_11_RADIO_AVS { ieee802_11_radio_avs_if_print, DLT_IEEE802_11_RADIO_AVS }, #endif #ifdef DLT_JUNIPER_ATM1 { juniper_atm1_print, DLT_JUNIPER_ATM1 }, #endif #ifdef DLT_JUNIPER_ATM2 { juniper_atm2_print, DLT_JUNIPER_ATM2 }, #endif #ifdef DLT_JUNIPER_MFR { juniper_mfr_print, DLT_JUNIPER_MFR }, #endif #ifdef DLT_JUNIPER_MLFR { juniper_mlfr_print, DLT_JUNIPER_MLFR }, #endif #ifdef DLT_JUNIPER_MLPPP { juniper_mlppp_print, DLT_JUNIPER_MLPPP }, #endif #ifdef DLT_JUNIPER_PPPOE { juniper_pppoe_print, DLT_JUNIPER_PPPOE }, #endif #ifdef DLT_JUNIPER_PPPOE_ATM { juniper_pppoe_atm_print, DLT_JUNIPER_PPPOE_ATM }, #endif #ifdef DLT_JUNIPER_GGSN { juniper_ggsn_print, DLT_JUNIPER_GGSN }, #endif #ifdef DLT_JUNIPER_ES { juniper_es_print, DLT_JUNIPER_ES }, #endif #ifdef DLT_JUNIPER_MONITOR { juniper_monitor_print, DLT_JUNIPER_MONITOR }, #endif #ifdef DLT_JUNIPER_SERVICES { juniper_services_print, DLT_JUNIPER_SERVICES }, #endif #ifdef DLT_JUNIPER_ETHER { juniper_ether_print, DLT_JUNIPER_ETHER }, #endif #ifdef DLT_JUNIPER_PPP { juniper_ppp_print, DLT_JUNIPER_PPP }, #endif #ifdef DLT_JUNIPER_FRELAY { juniper_frelay_print, DLT_JUNIPER_FRELAY }, #endif #ifdef DLT_JUNIPER_CHDLC { juniper_chdlc_print, DLT_JUNIPER_CHDLC }, #endif #ifdef DLT_MFR { mfr_if_print, DLT_MFR }, #endif #if defined(DLT_BLUETOOTH_HCI_H4_WITH_PHDR) && defined(HAVE_PCAP_BLUETOOTH_H) { bt_if_print, DLT_BLUETOOTH_HCI_H4_WITH_PHDR}, #endif #ifdef HAVE_PCAP_USB_H #ifdef DLT_USB_LINUX { usb_linux_48_byte_print, DLT_USB_LINUX}, #endif /* DLT_USB_LINUX */ #ifdef DLT_USB_LINUX_MMAPPED { usb_linux_64_byte_print, DLT_USB_LINUX_MMAPPED}, #endif /* DLT_USB_LINUX_MMAPPED */ #endif /* HAVE_PCAP_USB_H */ #ifdef DLT_IPV4 { raw_if_print, DLT_IPV4 }, #endif #ifdef DLT_IPV6 { raw_if_print, DLT_IPV6 }, #endif { NULL, 0 }, }; static struct ndo_printer ndo_printers[] = { { ether_if_print, DLT_EN10MB }, #ifdef DLT_IPNET { ipnet_if_print, DLT_IPNET }, #endif #ifdef DLT_IEEE802_15_4 { ieee802_15_4_if_print, DLT_IEEE802_15_4 }, #endif #ifdef DLT_IEEE802_15_4_NOFCS { ieee802_15_4_if_print, DLT_IEEE802_15_4_NOFCS }, #endif #ifdef DLT_PPI { ppi_if_print, DLT_PPI }, #endif #ifdef DLT_NETANALYZER { netanalyzer_if_print, DLT_NETANALYZER }, #endif #ifdef DLT_NETANALYZER_TRANSPARENT { netanalyzer_transparent_if_print, DLT_NETANALYZER_TRANSPARENT }, #endif { NULL, 0 }, }; if_printer lookup_printer(int type) { struct printer *p; for (p = printers; p->f; ++p) if (type == p->type) return p->f; return NULL; /* NOTREACHED */ } if_ndo_printer lookup_ndo_printer(int type) { struct ndo_printer *p; for (p = ndo_printers; p->f; ++p) if (type == p->type) return p->f; return NULL; /* NOTREACHED */ } static pcap_t *pd; static int supports_monitor_mode; extern int optind; extern int opterr; extern char *optarg; struct print_info { netdissect_options *ndo; union { if_printer printer; if_ndo_printer ndo_printer; } p; int ndo_type; }; struct dump_info { char *WFileName; char *CurrentFileName; pcap_t *pd; pcap_dumper_t *p; #ifdef __FreeBSD__ int dirfd; #endif }; #ifdef HAVE_PCAP_SET_TSTAMP_TYPE static void show_tstamp_types_and_exit(const char *device, pcap_t *pd) { int n_tstamp_types; int *tstamp_types = 0; const char *tstamp_type_name; int i; n_tstamp_types = pcap_list_tstamp_types(pd, &tstamp_types); if (n_tstamp_types < 0) error("%s", pcap_geterr(pd)); if (n_tstamp_types == 0) { fprintf(stderr, "Time stamp type cannot be set for %s\n", device); exit(0); } fprintf(stderr, "Time stamp types for %s (use option -j to set):\n", device); for (i = 0; i < n_tstamp_types; i++) { tstamp_type_name = pcap_tstamp_type_val_to_name(tstamp_types[i]); if (tstamp_type_name != NULL) { (void) fprintf(stderr, " %s (%s)\n", tstamp_type_name, pcap_tstamp_type_val_to_description(tstamp_types[i])); } else { (void) fprintf(stderr, " %d\n", tstamp_types[i]); } } pcap_free_tstamp_types(tstamp_types); exit(0); } #endif static void show_dlts_and_exit(const char *device, pcap_t *pd) { int n_dlts; int *dlts = 0; const char *dlt_name; n_dlts = pcap_list_datalinks(pd, &dlts); if (n_dlts < 0) error("%s", pcap_geterr(pd)); else if (n_dlts == 0 || !dlts) error("No data link types."); /* * If the interface is known to support monitor mode, indicate * whether these are the data link types available when not in * monitor mode, if -I wasn't specified, or when in monitor mode, * when -I was specified (the link-layer types available in * monitor mode might be different from the ones available when * not in monitor mode). */ if (supports_monitor_mode) (void) fprintf(stderr, "Data link types for %s %s (use option -y to set):\n", device, Iflag ? "when in monitor mode" : "when not in monitor mode"); else (void) fprintf(stderr, "Data link types for %s (use option -y to set):\n", device); while (--n_dlts >= 0) { dlt_name = pcap_datalink_val_to_name(dlts[n_dlts]); if (dlt_name != NULL) { (void) fprintf(stderr, " %s (%s)", dlt_name, pcap_datalink_val_to_description(dlts[n_dlts])); /* * OK, does tcpdump handle that type? */ if (lookup_printer(dlts[n_dlts]) == NULL && lookup_ndo_printer(dlts[n_dlts]) == NULL) (void) fprintf(stderr, " (printing not supported)"); fprintf(stderr, "\n"); } else { (void) fprintf(stderr, " DLT %d (printing not supported)\n", dlts[n_dlts]); } } #ifdef HAVE_PCAP_FREE_DATALINKS pcap_free_datalinks(dlts); #endif exit(0); } /* * Set up flags that might or might not be supported depending on the * version of libpcap we're using. */ #if defined(HAVE_PCAP_CREATE) || defined(WIN32) #define B_FLAG "B:" #define B_FLAG_USAGE " [ -B size ]" #else /* defined(HAVE_PCAP_CREATE) || defined(WIN32) */ #define B_FLAG #define B_FLAG_USAGE #endif /* defined(HAVE_PCAP_CREATE) || defined(WIN32) */ #ifdef HAVE_PCAP_CREATE #define I_FLAG "I" #else /* HAVE_PCAP_CREATE */ #define I_FLAG #endif /* HAVE_PCAP_CREATE */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE #define j_FLAG "j:" #define j_FLAG_USAGE " [ -j tstamptype ]" #define J_FLAG "J" #else /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ #define j_FLAG #define j_FLAG_USAGE #define J_FLAG #endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */ #ifdef HAVE_PCAP_FINDALLDEVS #ifndef HAVE_PCAP_IF_T #undef HAVE_PCAP_FINDALLDEVS #endif #endif #ifdef HAVE_PCAP_FINDALLDEVS #define D_FLAG "D" #else #define D_FLAG #endif #ifdef HAVE_PCAP_DUMP_FLUSH #define U_FLAG "U" #else #define U_FLAG #endif #ifndef WIN32 /* Drop root privileges and chroot if necessary */ static void droproot(const char *username, const char *chroot_dir) { struct passwd *pw = NULL; if (chroot_dir && !username) { fprintf(stderr, "tcpdump: Chroot without dropping root is insecure\n"); exit(1); } pw = getpwnam(username); if (pw) { if (chroot_dir) { if (chroot(chroot_dir) != 0 || chdir ("/") != 0) { fprintf(stderr, "tcpdump: Couldn't chroot/chdir to '%.64s': %s\n", chroot_dir, pcap_strerror(errno)); exit(1); } } #ifdef HAVE_CAP_NG_H int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG); if (ret < 0) { printf("error : ret %d\n", ret); } /* We don't need CAP_SETUID and CAP_SETGID */ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_SETUID); capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_SETUID); capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_SETUID); capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_SETUID); capng_apply(CAPNG_SELECT_BOTH); #else if (initgroups(pw->pw_name, pw->pw_gid) != 0 || setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) { fprintf(stderr, "tcpdump: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n", username, (unsigned long)pw->pw_uid, (unsigned long)pw->pw_gid, pcap_strerror(errno)); exit(1); } #endif /* HAVE_CAP_NG_H */ } else { fprintf(stderr, "tcpdump: Couldn't find user '%.32s'\n", username); exit(1); } } #endif /* WIN32 */ static int getWflagChars(int x) { int c = 0; x -= 1; while (x > 0) { c += 1; x /= 10; } return c; } static void MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars) { char *filename = malloc(PATH_MAX + 1); if (filename == NULL) error("Makefilename: malloc"); /* Process with strftime if Gflag is set. */ if (Gflag != 0) { struct tm *local_tm; /* Convert Gflag_time to a usable format */ if ((local_tm = localtime(&Gflag_time)) == NULL) { error("MakeTimedFilename: localtime"); } /* There's no good way to detect an error in strftime since a return * value of 0 isn't necessarily failure. */ strftime(filename, PATH_MAX, orig_name, local_tm); } else { strncpy(filename, orig_name, PATH_MAX); } if (cnt == 0 && max_chars == 0) strncpy(buffer, filename, PATH_MAX + 1); else if (snprintf(buffer, PATH_MAX + 1, "%s%0*d", filename, max_chars, cnt) > PATH_MAX) /* Report an error if the filename is too large */ error("too many output files or filename is too long (> %d)", PATH_MAX); free(filename); } static int tcpdump_printf(netdissect_options *ndo _U_, const char *fmt, ...) { va_list args; int ret; va_start(args, fmt); ret=vfprintf(stdout, fmt, args); va_end(args); return ret; } static struct print_info get_print_info(int type) { struct print_info printinfo; printinfo.ndo_type = 1; printinfo.ndo = gndo; printinfo.p.ndo_printer = lookup_ndo_printer(type); if (printinfo.p.ndo_printer == NULL) { printinfo.p.printer = lookup_printer(type); printinfo.ndo_type = 0; if (printinfo.p.printer == NULL) { gndo->ndo_dltname = pcap_datalink_val_to_name(type); if (gndo->ndo_dltname != NULL) error("packet printing is not supported for link type %s: use -w", gndo->ndo_dltname); else error("packet printing is not supported for link type %d: use -w", type); } } return (printinfo); } static char * get_next_file(FILE *VFile, char *ptr) { char *ret; ret = fgets(ptr, PATH_MAX, VFile); if (!ret) return NULL; if (ptr[strlen(ptr) - 1] == '\n') ptr[strlen(ptr) - 1] = '\0'; return ret; } int main(int argc, char **argv) { register int cnt, op, i; bpf_u_int32 localnet, netmask; register char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName; pcap_handler callback; int type; int dlt; int new_dlt; const char *dlt_name; struct bpf_program fcode; #ifndef WIN32 RETSIGTYPE (*oldhandler)(int); #endif struct print_info printinfo; struct dump_info dumpinfo; u_char *pcap_userdata; char ebuf[PCAP_ERRBUF_SIZE]; char VFileLine[PATH_MAX + 1]; char *username = NULL; char *chroot_dir = NULL; char *ret = NULL; char *end; #ifdef HAVE_PCAP_FINDALLDEVS pcap_if_t *devpointer; int devnum; #endif int status; FILE *VFile; #ifdef __FreeBSD__ cap_rights_t rights; int cansandbox; #endif /* __FreeBSD__ */ #ifdef WIN32 if(wsockinit() != 0) return 1; #endif /* WIN32 */ jflag=-1; /* not set */ gndo->ndo_Oflag=1; gndo->ndo_Rflag=1; gndo->ndo_dlt=-1; gndo->ndo_default_print=ndo_default_print; gndo->ndo_printf=tcpdump_printf; gndo->ndo_error=ndo_error; gndo->ndo_warning=ndo_warning; gndo->ndo_snaplen = DEFAULT_SNAPLEN; cnt = -1; device = NULL; infile = NULL; RFileName = NULL; VFileName = NULL; VFile = NULL; WFileName = NULL; dlt = -1; if ((cp = strrchr(argv[0], '/')) != NULL) program_name = cp + 1; else program_name = argv[0]; if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0) error("%s", ebuf); #ifdef LIBSMI smiInit("tcpdump"); #endif while ( (op = getopt(argc, argv, "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpqr:Rs:StT:u" U_FLAG "V:vw:W:xXy:Yz:Z:")) != -1) switch (op) { case 'a': /* compatibility for old -a */ break; case 'A': ++Aflag; break; case 'b': ++bflag; break; #if defined(HAVE_PCAP_CREATE) || defined(WIN32) case 'B': Bflag = atoi(optarg)*1024; if (Bflag <= 0) error("invalid packet buffer size %s", optarg); break; #endif /* defined(HAVE_PCAP_CREATE) || defined(WIN32) */ case 'c': cnt = atoi(optarg); if (cnt <= 0) error("invalid packet count %s", optarg); break; case 'C': Cflag = atoi(optarg) * 1000000; if (Cflag < 0) error("invalid file size %s", optarg); break; case 'd': ++dflag; break; #ifdef HAVE_PCAP_FINDALLDEVS case 'D': if (pcap_findalldevs(&devpointer, ebuf) < 0) error("%s", ebuf); else { for (i = 0; devpointer != 0; i++) { printf("%d.%s", i+1, devpointer->name); if (devpointer->description != NULL) printf(" (%s)", devpointer->description); printf("\n"); devpointer = devpointer->next; } } return 0; #endif /* HAVE_PCAP_FINDALLDEVS */ case 'L': Lflag++; break; case 'e': ++eflag; break; case 'E': #ifndef HAVE_LIBCRYPTO warning("crypto code not compiled in"); #endif gndo->ndo_espsecret = optarg; break; case 'f': ++fflag; break; case 'F': infile = optarg; break; case 'G': Gflag = atoi(optarg); if (Gflag < 0) error("invalid number of seconds %s", optarg); /* We will create one file initially. */ Gflag_count = 0; /* Grab the current time for rotation use. */ if ((Gflag_time = time(NULL)) == (time_t)-1) { error("main: can't get current time: %s", pcap_strerror(errno)); } break; case 'h': usage(); break; case 'H': ++Hflag; break; case 'i': if (optarg[0] == '0' && optarg[1] == 0) error("Invalid adapter index"); #ifdef HAVE_PCAP_FINDALLDEVS /* * If the argument is a number, treat it as * an index into the list of adapters, as * printed by "tcpdump -D". * * This should be OK on UNIX systems, as interfaces * shouldn't have names that begin with digits. * It can be useful on Windows, where more than * one interface can have the same name. */ devnum = strtol(optarg, &end, 10); if (optarg != end && *end == '\0') { if (devnum < 0) error("Invalid adapter index"); if (pcap_findalldevs(&devpointer, ebuf) < 0) error("%s", ebuf); else { /* * Look for the devnum-th entry * in the list of devices * (1-based). */ for (i = 0; i < devnum-1 && devpointer != NULL; i++, devpointer = devpointer->next) ; if (devpointer == NULL) error("Invalid adapter index"); } device = devpointer->name; break; } #endif /* HAVE_PCAP_FINDALLDEVS */ device = optarg; break; #ifdef HAVE_PCAP_CREATE case 'I': ++Iflag; break; #endif /* HAVE_PCAP_CREATE */ #ifdef HAVE_PCAP_SET_TSTAMP_TYPE case 'j': jflag = pcap_tstamp_type_name_to_val(optarg); if (jflag < 0) error("invalid time stamp type %s", optarg); break; case 'J': Jflag++; break; #endif case 'l': #ifdef WIN32 /* * _IOLBF is the same as _IOFBF in Microsoft's C * libraries; the only alternative they offer * is _IONBF. * * XXX - this should really be checking for MSVC++, * not WIN32, if, for example, MinGW has its own * C library that is more UNIX-compatible. */ setvbuf(stdout, NULL, _IONBF, 0); #else /* WIN32 */ #ifdef HAVE_SETLINEBUF setlinebuf(stdout); #else setvbuf(stdout, NULL, _IOLBF, 0); #endif #endif /* WIN32 */ break; case 'K': ++Kflag; break; case 'm': #ifdef LIBSMI if (smiLoadModule(optarg) == 0) { error("could not load MIB module %s", optarg); } sflag = 1; #else (void)fprintf(stderr, "%s: ignoring option `-m %s' ", program_name, optarg); (void)fprintf(stderr, "(no libsmi support)\n"); #endif break; case 'M': /* TCP-MD5 shared secret */ #ifndef HAVE_LIBCRYPTO warning("crypto code not compiled in"); #endif sigsecret = optarg; break; case 'n': ++nflag; break; case 'N': ++Nflag; break; case 'O': Oflag = 0; break; case 'p': ++pflag; break; case 'q': ++qflag; ++suppress_default_print; break; case 'r': RFileName = optarg; break; case 'R': Rflag = 0; break; case 's': snaplen = strtol(optarg, &end, 0); if (optarg == end || *end != '\0' || snaplen < 0 || snaplen > MAXIMUM_SNAPLEN) error("invalid snaplen %s", optarg); else if (snaplen == 0) snaplen = MAXIMUM_SNAPLEN; break; case 'S': ++Sflag; break; case 't': ++tflag; break; case 'T': if (strcasecmp(optarg, "vat") == 0) packettype = PT_VAT; else if (strcasecmp(optarg, "wb") == 0) packettype = PT_WB; else if (strcasecmp(optarg, "rpc") == 0) packettype = PT_RPC; else if (strcasecmp(optarg, "rtp") == 0) packettype = PT_RTP; else if (strcasecmp(optarg, "rtcp") == 0) packettype = PT_RTCP; else if (strcasecmp(optarg, "snmp") == 0) packettype = PT_SNMP; else if (strcasecmp(optarg, "cnfp") == 0) packettype = PT_CNFP; else if (strcasecmp(optarg, "tftp") == 0) packettype = PT_TFTP; else if (strcasecmp(optarg, "aodv") == 0) packettype = PT_AODV; else if (strcasecmp(optarg, "carp") == 0) packettype = PT_CARP; else if (strcasecmp(optarg, "radius") == 0) packettype = PT_RADIUS; else if (strcasecmp(optarg, "zmtp1") == 0) packettype = PT_ZMTP1; else if (strcasecmp(optarg, "vxlan") == 0) packettype = PT_VXLAN; else error("unknown packet type `%s'", optarg); break; case 'u': ++uflag; break; #ifdef HAVE_PCAP_DUMP_FLUSH case 'U': ++Uflag; break; #endif case 'v': ++vflag; break; case 'V': VFileName = optarg; break; case 'w': WFileName = optarg; break; case 'W': Wflag = atoi(optarg); if (Wflag < 0) error("invalid number of output files %s", optarg); WflagChars = getWflagChars(Wflag); break; case 'x': ++xflag; ++suppress_default_print; break; case 'X': ++Xflag; ++suppress_default_print; break; case 'y': gndo->ndo_dltname = optarg; gndo->ndo_dlt = pcap_datalink_name_to_val(gndo->ndo_dltname); if (gndo->ndo_dlt < 0) error("invalid data link type %s", gndo->ndo_dltname); break; #if defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG) case 'Y': { /* Undocumented flag */ #ifdef HAVE_PCAP_DEBUG extern int pcap_debug; pcap_debug = 1; #else extern int yydebug; yydebug = 1; #endif } break; #endif case 'z': if (optarg) { zflag = strdup(optarg); } else { usage(); /* NOTREACHED */ } break; case 'Z': if (optarg) { username = strdup(optarg); } else { usage(); /* NOTREACHED */ } break; default: usage(); /* NOTREACHED */ } switch (tflag) { case 0: /* Default */ case 4: /* Default + Date*/ thiszone = gmt2local(0); break; case 1: /* No time stamp */ case 2: /* Unix timeval style */ case 3: /* Microseconds since previous packet */ case 5: /* Microseconds since first packet */ break; default: /* Not supported */ error("only -t, -tt, -ttt, -tttt and -ttttt are supported"); break; } if (fflag != 0 && (VFileName != NULL || RFileName != NULL)) error("-f can not be used with -V or -r"); if (VFileName != NULL && RFileName != NULL) error("-V and -r are mutually exclusive."); #ifdef WITH_CHROOT /* if run as root, prepare for chrooting */ if (getuid() == 0 || geteuid() == 0) { /* future extensibility for cmd-line arguments */ if (!chroot_dir) chroot_dir = WITH_CHROOT; } #endif #ifdef WITH_USER /* if run as root, prepare for dropping root privileges */ if (getuid() == 0 || geteuid() == 0) { /* Run with '-Z root' to restore old behaviour */ if (!username) username = WITH_USER; } #endif if (RFileName != NULL || VFileName != NULL) { /* * If RFileName is non-null, it's the pathname of a * savefile to read. If VFileName is non-null, it's * the pathname of a file containing a list of pathnames * (one per line) of savefiles to read. * * In either case, we're reading a savefile, not doing * a live capture. */ #ifndef WIN32 /* * We don't need network access, so relinquish any set-UID * or set-GID privileges we have (if any). * * We do *not* want set-UID privileges when opening a * trace file, as that might let the user read other * people's trace files (especially if we're set-UID * root). */ if (setgid(getgid()) != 0 || setuid(getuid()) != 0 ) fprintf(stderr, "Warning: setgid/setuid failed !\n"); #endif /* WIN32 */ if (VFileName != NULL) { if (VFileName[0] == '-' && VFileName[1] == '\0') VFile = stdin; else VFile = fopen(VFileName, "r"); if (VFile == NULL) error("Unable to open file: %s\n", strerror(errno)); ret = get_next_file(VFile, VFileLine); if (!ret) error("Nothing in %s\n", VFileName); RFileName = VFileLine; } pd = pcap_open_offline(RFileName, ebuf); if (pd == NULL) error("%s", ebuf); #ifdef __FreeBSD__ cap_rights_init(&rights, CAP_READ); if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 && errno != ENOSYS) { error("unable to limit pcap descriptor"); } #endif dlt = pcap_datalink(pd); dlt_name = pcap_datalink_val_to_name(dlt); if (dlt_name == NULL) { fprintf(stderr, "reading from file %s, link-type %u\n", RFileName, dlt); } else { fprintf(stderr, "reading from file %s, link-type %s (%s)\n", RFileName, dlt_name, pcap_datalink_val_to_description(dlt)); } localnet = 0; netmask = 0; } else { /* * We're doing a live capture. */ if (device == NULL) { device = pcap_lookupdev(ebuf); if (device == NULL) error("%s", ebuf); } #ifdef WIN32 /* * Print a message to the standard error on Windows. * XXX - why do it here, with a different message? */ if(strlen(device) == 1) //we assume that an ASCII string is always longer than 1 char { //a Unicode string has a \0 as second byte (so strlen() is 1) fprintf(stderr, "%s: listening on %ws\n", program_name, device); } else { fprintf(stderr, "%s: listening on %s\n", program_name, device); } fflush(stderr); #endif /* WIN32 */ #ifdef HAVE_PCAP_CREATE pd = pcap_create(device, ebuf); if (pd == NULL) error("%s", ebuf); #ifdef HAVE_PCAP_SET_TSTAMP_TYPE if (Jflag) show_tstamp_types_and_exit(device, pd); #endif /* * Is this an interface that supports monitor mode? */ if (pcap_can_set_rfmon(pd) == 1) supports_monitor_mode = 1; else supports_monitor_mode = 0; status = pcap_set_snaplen(pd, snaplen); if (status != 0) error("%s: Can't set snapshot length: %s", device, pcap_statustostr(status)); status = pcap_set_promisc(pd, !pflag); if (status != 0) error("%s: Can't set promiscuous mode: %s", device, pcap_statustostr(status)); if (Iflag) { status = pcap_set_rfmon(pd, 1); if (status != 0) error("%s: Can't set monitor mode: %s", device, pcap_statustostr(status)); } status = pcap_set_timeout(pd, 1000); if (status != 0) error("%s: pcap_set_timeout failed: %s", device, pcap_statustostr(status)); if (Bflag != 0) { status = pcap_set_buffer_size(pd, Bflag); if (status != 0) error("%s: Can't set buffer size: %s", device, pcap_statustostr(status)); } #ifdef HAVE_PCAP_SET_TSTAMP_TYPE if (jflag != -1) { status = pcap_set_tstamp_type(pd, jflag); if (status < 0) error("%s: Can't set time stamp type: %s", device, pcap_statustostr(status)); } #endif status = pcap_activate(pd); if (status < 0) { /* * pcap_activate() failed. */ cp = pcap_geterr(pd); if (status == PCAP_ERROR) error("%s", cp); else if ((status == PCAP_ERROR_NO_SUCH_DEVICE || status == PCAP_ERROR_PERM_DENIED) && *cp != '\0') error("%s: %s\n(%s)", device, pcap_statustostr(status), cp); #ifdef __FreeBSD__ else if (status == PCAP_ERROR_RFMON_NOTSUP && strncmp(device, "wlan", 4) == 0) { char parent[8], newdev[8]; char sysctl[32]; size_t s = sizeof(parent); snprintf(sysctl, sizeof(sysctl), "net.wlan.%d.%%parent", atoi(device + 4)); sysctlbyname(sysctl, parent, &s, NULL, 0); strlcpy(newdev, device, sizeof(newdev)); /* Suggest a new wlan device. */ newdev[strlen(newdev)-1]++; error("%s is not a monitor mode VAP\n" "To create a new monitor mode VAP use:\n" " ifconfig %s create wlandev %s wlanmode " "monitor\nand use %s as the tcpdump " "interface", device, newdev, parent, newdev); } #endif else error("%s: %s", device, pcap_statustostr(status)); } else if (status > 0) { /* * pcap_activate() succeeded, but it's warning us * of a problem it had. */ cp = pcap_geterr(pd); if (status == PCAP_WARNING) warning("%s", cp); else if (status == PCAP_WARNING_PROMISC_NOTSUP && *cp != '\0') warning("%s: %s\n(%s)", device, pcap_statustostr(status), cp); else warning("%s: %s", device, pcap_statustostr(status)); } #else *ebuf = '\0'; pd = pcap_open_live(device, snaplen, !pflag, 1000, ebuf); if (pd == NULL) error("%s", ebuf); else if (*ebuf) warning("%s", ebuf); #endif /* HAVE_PCAP_CREATE */ /* * Let user own process after socket has been opened. */ #ifndef WIN32 if (setgid(getgid()) != 0 || setuid(getuid()) != 0) fprintf(stderr, "Warning: setgid/setuid failed !\n"); #endif /* WIN32 */ #if !defined(HAVE_PCAP_CREATE) && defined(WIN32) if(Bflag != 0) if(pcap_setbuff(pd, Bflag)==-1){ error("%s", pcap_geterr(pd)); } #endif /* !defined(HAVE_PCAP_CREATE) && defined(WIN32) */ if (Lflag) show_dlts_and_exit(device, pd); if (gndo->ndo_dlt >= 0) { #ifdef HAVE_PCAP_SET_DATALINK if (pcap_set_datalink(pd, gndo->ndo_dlt) < 0) error("%s", pcap_geterr(pd)); #else /* * We don't actually support changing the * data link type, so we only let them * set it to what it already is. */ if (gndo->ndo_dlt != pcap_datalink(pd)) { error("%s is not one of the DLTs supported by this device\n", gndo->ndo_dltname); } #endif (void)fprintf(stderr, "%s: data link type %s\n", program_name, gndo->ndo_dltname); (void)fflush(stderr); } i = pcap_snapshot(pd); if (snaplen < i) { warning("snaplen raised from %d to %d", snaplen, i); snaplen = i; } if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) { localnet = 0; netmask = 0; warning("%s", ebuf); } } if (infile) cmdbuf = read_infile(infile); else cmdbuf = copy_argv(&argv[optind]); if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); if (dflag) { bpf_dump(&fcode, dflag); pcap_close(pd); free(cmdbuf); exit(0); } init_addrtoname(localnet, netmask); init_checksum(); #ifndef WIN32 (void)setsignal(SIGPIPE, cleanup); (void)setsignal(SIGTERM, cleanup); (void)setsignal(SIGINT, cleanup); #endif /* WIN32 */ #if defined(HAVE_FORK) || defined(HAVE_VFORK) (void)setsignal(SIGCHLD, child_cleanup); #endif /* Cooperate with nohup(1) */ #ifndef WIN32 if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL) (void)setsignal(SIGHUP, oldhandler); #endif /* WIN32 */ #ifndef WIN32 /* * If a user name was specified with "-Z", attempt to switch to * that user's UID. This would probably be used with sudo, * to allow tcpdump to be run in a special restricted * account (if you just want to allow users to open capture * devices, and can't just give users that permission, * you'd make tcpdump set-UID or set-GID). * * Tcpdump doesn't necessarily write only to one savefile; * the general only way to allow a -Z instance to write to * savefiles as the user under whose UID it's run, rather * than as the user specified with -Z, would thus be to switch * to the original user ID before opening a capture file and * then switch back to the -Z user ID after opening the savefile. * Switching to the -Z user ID only after opening the first * savefile doesn't handle the general case. */ #ifdef HAVE_CAP_NG_H /* We are running as root and we will be writing to savefile */ if ((getuid() == 0 || geteuid() == 0) && WFileName) { if (username) { /* Drop all capabilities from effective set */ capng_clear(CAPNG_EFFECTIVE); /* Add capabilities we will need*/ capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_SETUID); capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_SETGID); capng_update(CAPNG_ADD, CAPNG_PERMITTED, CAP_DAC_OVERRIDE); capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_SETUID); capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_SETGID); capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); capng_apply(CAPNG_SELECT_BOTH); } } #endif /* HAVE_CAP_NG_H */ if (getuid() == 0 || geteuid() == 0) { if (username || chroot_dir) droproot(username, chroot_dir); } #endif /* WIN32 */ if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); #ifdef __FreeBSD__ if (RFileName == NULL && VFileName == NULL) { static const unsigned long cmds[] = { BIOCGSTATS }; cap_rights_init(&rights, CAP_IOCTL, CAP_READ); if (cap_rights_limit(pcap_fileno(pd), &rights) < 0 && errno != ENOSYS) { error("unable to limit pcap descriptor"); } if (cap_ioctls_limit(pcap_fileno(pd), cmds, sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) { error("unable to limit ioctls on pcap descriptor"); } } #endif if (WFileName) { pcap_dumper_t *p; /* Do not exceed the default PATH_MAX for files. */ dumpinfo.CurrentFileName = (char *)malloc(PATH_MAX + 1); if (dumpinfo.CurrentFileName == NULL) error("malloc of dumpinfo.CurrentFileName"); /* We do not need numbering for dumpfiles if Cflag isn't set. */ if (Cflag != 0) MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, WflagChars); else MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0); p = pcap_dump_open(pd, dumpinfo.CurrentFileName); #ifdef HAVE_CAP_NG_H /* Give up capabilities, clear Effective set */ capng_clear(CAPNG_EFFECTIVE); #endif if (p == NULL) error("%s", pcap_geterr(pd)); #ifdef __FreeBSD__ cap_rights_init(&rights, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(fileno(pcap_dump_file(p)), &rights) < 0 && errno != ENOSYS) { error("unable to limit dump descriptor"); } #endif if (Cflag != 0 || Gflag != 0) { #ifdef __FreeBSD__ dumpinfo.WFileName = strdup(basename(WFileName)); dumpinfo.dirfd = open(dirname(WFileName), O_DIRECTORY | O_RDONLY); if (dumpinfo.dirfd < 0) { error("unable to open directory %s", dirname(WFileName)); } cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL, CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 && errno != ENOSYS) { error("unable to limit directory rights"); } #else /* !__FreeBSD__ */ dumpinfo.WFileName = WFileName; #endif callback = dump_packet_and_trunc; dumpinfo.pd = pd; dumpinfo.p = p; pcap_userdata = (u_char *)&dumpinfo; } else { callback = dump_packet; pcap_userdata = (u_char *)p; } #ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) pcap_dump_flush(p); #endif } else { type = pcap_datalink(pd); printinfo = get_print_info(type); callback = print_packet; pcap_userdata = (u_char *)&printinfo; } #ifdef SIGNAL_REQ_INFO /* * We can't get statistics when reading from a file rather * than capturing from a device. */ if (RFileName == NULL) (void)setsignal(SIGNAL_REQ_INFO, requestinfo); #endif if (vflag > 0 && WFileName) { /* * When capturing to a file, "-v" means tcpdump should, * every 10 secodns, "v"erbosely report the number of * packets captured. */ #ifdef USE_WIN32_MM_TIMER /* call verbose_stats_dump() each 1000 +/-100msec */ timer_id = timeSetEvent(1000, 100, verbose_stats_dump, 0, TIME_PERIODIC); setvbuf(stderr, NULL, _IONBF, 0); #elif defined(HAVE_ALARM) (void)setsignal(SIGALRM, verbose_stats_dump); alarm(1); #endif } #ifndef WIN32 if (RFileName == NULL) { /* * Live capture (if -V was specified, we set RFileName * to a file from the -V file). Print a message to * the standard error on UN*X. */ if (!vflag && !WFileName) { (void)fprintf(stderr, "%s: verbose output suppressed, use -v or -vv for full protocol decode\n", program_name); } else (void)fprintf(stderr, "%s: ", program_name); dlt = pcap_datalink(pd); dlt_name = pcap_datalink_val_to_name(dlt); if (dlt_name == NULL) { (void)fprintf(stderr, "listening on %s, link-type %u, capture size %u bytes\n", device, dlt, snaplen); } else { (void)fprintf(stderr, "listening on %s, link-type %s (%s), capture size %u bytes\n", device, dlt_name, pcap_datalink_val_to_description(dlt), snaplen); } (void)fflush(stderr); } #endif /* WIN32 */ #ifdef __FreeBSD__ cansandbox = (nflag && VFileName == NULL && zflag == NULL); if (cansandbox && cap_enter() < 0 && errno != ENOSYS) error("unable to enter the capability mode"); if (cap_sandboxed()) fprintf(stderr, "capability mode sandbox enabled\n"); #endif /* __FreeBSD__ */ do { status = pcap_loop(pd, cnt, callback, pcap_userdata); if (WFileName == NULL) { /* * We're printing packets. Flush the printed output, * so it doesn't get intermingled with error output. */ if (status == -2) { /* * We got interrupted, so perhaps we didn't * manage to finish a line we were printing. * Print an extra newline, just in case. */ putchar('\n'); } (void)fflush(stdout); } if (status == -1) { /* * Error. Report it. */ (void)fprintf(stderr, "%s: pcap_loop: %s\n", program_name, pcap_geterr(pd)); } if (RFileName == NULL) { /* * We're doing a live capture. Report the capture * statistics. */ info(1); } pcap_close(pd); if (VFileName != NULL) { ret = get_next_file(VFile, VFileLine); if (ret) { RFileName = VFileLine; pd = pcap_open_offline(RFileName, ebuf); if (pd == NULL) error("%s", ebuf); #ifdef __FreeBSD__ cap_rights_init(&rights, CAP_READ); if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 && errno != ENOSYS) { error("unable to limit pcap descriptor"); } #endif new_dlt = pcap_datalink(pd); if (WFileName && new_dlt != dlt) error("%s: new dlt does not match original", RFileName); printinfo = get_print_info(new_dlt); dlt_name = pcap_datalink_val_to_name(new_dlt); if (dlt_name == NULL) { fprintf(stderr, "reading from file %s, link-type %u\n", RFileName, new_dlt); } else { fprintf(stderr, "reading from file %s, link-type %s (%s)\n", RFileName, dlt_name, pcap_datalink_val_to_description(new_dlt)); } if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0) error("%s", pcap_geterr(pd)); if (pcap_setfilter(pd, &fcode) < 0) error("%s", pcap_geterr(pd)); } } } while (ret != NULL); free(cmdbuf); exit(status == -1 ? 1 : 0); } /* make a clean exit on interrupts */ static RETSIGTYPE cleanup(int signo _U_) { #ifdef USE_WIN32_MM_TIMER if (timer_id) timeKillEvent(timer_id); timer_id = 0; #elif defined(HAVE_ALARM) alarm(0); #endif #ifdef HAVE_PCAP_BREAKLOOP /* * We have "pcap_breakloop()"; use it, so that we do as little * as possible in the signal handler (it's probably not safe * to do anything with standard I/O streams in a signal handler - * the ANSI C standard doesn't say it is). */ pcap_breakloop(pd); #else /* * We don't have "pcap_breakloop()"; this isn't safe, but * it's the best we can do. Print the summary if we're * not reading from a savefile - i.e., if we're doing a * live capture - and exit. */ if (pd != NULL && pcap_file(pd) == NULL) { /* * We got interrupted, so perhaps we didn't * manage to finish a line we were printing. * Print an extra newline, just in case. */ putchar('\n'); (void)fflush(stdout); info(1); } exit(0); #endif } /* On windows, we do not use a fork, so we do not care less about waiting a child processes to die */ #if defined(HAVE_FORK) || defined(HAVE_VFORK) static RETSIGTYPE child_cleanup(int signo _U_) { wait(NULL); } #endif /* HAVE_FORK && HAVE_VFORK */ static void info(register int verbose) { struct pcap_stat stat; /* * Older versions of libpcap didn't set ps_ifdrop on some * platforms; initialize it to 0 to handle that. */ stat.ps_ifdrop = 0; if (pcap_stats(pd, &stat) < 0) { (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd)); infoprint = 0; return; } if (!verbose) fprintf(stderr, "%s: ", program_name); (void)fprintf(stderr, "%u packet%s captured", packets_captured, PLURAL_SUFFIX(packets_captured)); if (!verbose) fputs(", ", stderr); else putc('\n', stderr); (void)fprintf(stderr, "%u packet%s received by filter", stat.ps_recv, PLURAL_SUFFIX(stat.ps_recv)); if (!verbose) fputs(", ", stderr); else putc('\n', stderr); (void)fprintf(stderr, "%u packet%s dropped by kernel", stat.ps_drop, PLURAL_SUFFIX(stat.ps_drop)); if (stat.ps_ifdrop != 0) { if (!verbose) fputs(", ", stderr); else putc('\n', stderr); (void)fprintf(stderr, "%u packet%s dropped by interface\n", stat.ps_ifdrop, PLURAL_SUFFIX(stat.ps_ifdrop)); } else putc('\n', stderr); infoprint = 0; } #if defined(HAVE_FORK) || defined(HAVE_VFORK) static void compress_savefile(const char *filename) { # ifdef HAVE_FORK if (fork()) # else if (vfork()) # endif return; /* * Set to lowest priority so that this doesn't disturb the capture */ #ifdef NZERO setpriority(PRIO_PROCESS, 0, NZERO - 1); #else setpriority(PRIO_PROCESS, 0, 19); #endif if (execlp(zflag, zflag, filename, (char *)NULL) == -1) fprintf(stderr, "compress_savefile:execlp(%s, %s): %s\n", zflag, filename, strerror(errno)); # ifdef HAVE_FORK exit(1); # else _exit(1); # endif } #else /* HAVE_FORK && HAVE_VFORK */ static void compress_savefile(const char *filename) { fprintf(stderr, "compress_savefile failed. Functionality not implemented under your system\n"); } #endif /* HAVE_FORK && HAVE_VFORK */ static void dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { struct dump_info *dump_info; #ifdef __FreeBSD__ cap_rights_t rights; #endif ++packets_captured; ++infodelay; dump_info = (struct dump_info *)user; /* * XXX - this won't force the file to rotate on the specified time * boundary, but it will rotate on the first packet received after the * specified Gflag number of seconds. Note: if a Gflag time boundary * and a Cflag size boundary coincide, the time rotation will occur * first thereby cancelling the Cflag boundary (since the file should * be 0). */ if (Gflag != 0) { /* Check if it is time to rotate */ time_t t; /* Get the current time */ if ((t = time(NULL)) == (time_t)-1) { error("dump_and_trunc_packet: can't get current_time: %s", pcap_strerror(errno)); } /* If the time is greater than the specified window, rotate */ if (t - Gflag_time >= Gflag) { #ifdef __FreeBSD__ FILE *fp; int fd; #endif /* Update the Gflag_time */ Gflag_time = t; /* Update Gflag_count */ Gflag_count++; /* * Close the current file and open a new one. */ pcap_dump_close(dump_info->p); /* * Compress the file we just closed, if the user asked for it */ if (zflag != NULL) compress_savefile(dump_info->CurrentFileName); /* * Check to see if we've exceeded the Wflag (when * not using Cflag). */ if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag) { (void)fprintf(stderr, "Maximum file limit reached: %d\n", Wflag); exit(0); /* NOTREACHED */ } if (dump_info->CurrentFileName != NULL) free(dump_info->CurrentFileName); /* Allocate space for max filename + \0. */ dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1); if (dump_info->CurrentFileName == NULL) error("dump_packet_and_trunc: malloc"); /* * This is always the first file in the Cflag * rotation: e.g. 0 * We also don't need numbering if Cflag is not set. */ if (Cflag != 0) MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, WflagChars); else MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0); #ifdef HAVE_CAP_NG_H capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); capng_apply(CAPNG_EFFECTIVE); #endif /* HAVE_CAP_NG_H */ #ifdef __FreeBSD__ fd = openat(dump_info->dirfd, dump_info->CurrentFileName, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { error("unable to open file %s", dump_info->CurrentFileName); } fp = fdopen(fd, "w"); if (fp == NULL) { error("unable to fdopen file %s", dump_info->CurrentFileName); } dump_info->p = pcap_dump_fopen(dump_info->pd, fp); #else /* !__FreeBSD__ */ dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName); #endif #ifdef HAVE_CAP_NG_H capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE); capng_apply(CAPNG_EFFECTIVE); #endif /* HAVE_CAP_NG_H */ if (dump_info->p == NULL) error("%s", pcap_geterr(pd)); #ifdef __FreeBSD__ cap_rights_init(&rights, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(fileno(pcap_dump_file(dump_info->p)), &rights) < 0 && errno != ENOSYS) { error("unable to limit dump descriptor"); } #endif } } /* * XXX - this won't prevent capture files from getting * larger than Cflag - the last packet written to the * file could put it over Cflag. */ if (Cflag != 0 && pcap_dump_ftell(dump_info->p) > Cflag) { #ifdef __FreeBSD__ FILE *fp; int fd; #endif /* * Close the current file and open a new one. */ pcap_dump_close(dump_info->p); /* * Compress the file we just closed, if the user asked for it */ if (zflag != NULL) compress_savefile(dump_info->CurrentFileName); Cflag_count++; if (Wflag > 0) { if (Cflag_count >= Wflag) Cflag_count = 0; } if (dump_info->CurrentFileName != NULL) free(dump_info->CurrentFileName); dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1); if (dump_info->CurrentFileName == NULL) error("dump_packet_and_trunc: malloc"); MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars); #ifdef __FreeBSD__ fd = openat(dump_info->dirfd, dump_info->CurrentFileName, O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { error("unable to open file %s", dump_info->CurrentFileName); } fp = fdopen(fd, "w"); if (fp == NULL) { error("unable to fdopen file %s", dump_info->CurrentFileName); } dump_info->p = pcap_dump_fopen(dump_info->pd, fp); #else /* !__FreeBSD__ */ dump_info->p = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName); #endif if (dump_info->p == NULL) error("%s", pcap_geterr(pd)); #ifdef __FreeBSD__ cap_rights_init(&rights, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(fileno(pcap_dump_file(dump_info->p)), &rights) < 0 && errno != ENOSYS) { error("unable to limit dump descriptor"); } #endif } pcap_dump((u_char *)dump_info->p, h, sp); #ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) pcap_dump_flush(dump_info->p); #endif --infodelay; if (infoprint) info(0); } static void dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { ++packets_captured; ++infodelay; pcap_dump(user, h, sp); #ifdef HAVE_PCAP_DUMP_FLUSH if (Uflag) pcap_dump_flush((pcap_dumper_t *)user); #endif --infodelay; if (infoprint) info(0); } static void print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) { struct print_info *print_info; u_int hdrlen; ++packets_captured; ++infodelay; ts_print(&h->ts); print_info = (struct print_info *)user; /* * Some printers want to check that they're not walking off the * end of the packet. * Rather than pass it all the way down, we set this global. */ snapend = sp + h->caplen; if(print_info->ndo_type) { hdrlen = (*print_info->p.ndo_printer)(print_info->ndo, h, sp); } else { hdrlen = (*print_info->p.printer)(h, sp); } if (Xflag) { /* * Print the raw packet data in hex and ASCII. */ if (Xflag > 1) { /* * Include the link-layer header. */ hex_and_ascii_print("\n\t", sp, h->caplen); } else { /* * Don't include the link-layer header - and if * we have nothing past the link-layer header, * print nothing. */ if (h->caplen > hdrlen) hex_and_ascii_print("\n\t", sp + hdrlen, h->caplen - hdrlen); } } else if (xflag) { /* * Print the raw packet data in hex. */ if (xflag > 1) { /* * Include the link-layer header. */ hex_print("\n\t", sp, h->caplen); } else { /* * Don't include the link-layer header - and if * we have nothing past the link-layer header, * print nothing. */ if (h->caplen > hdrlen) hex_print("\n\t", sp + hdrlen, h->caplen - hdrlen); } } else if (Aflag) { /* * Print the raw packet data in ASCII. */ if (Aflag > 1) { /* * Include the link-layer header. */ ascii_print(sp, h->caplen); } else { /* * Don't include the link-layer header - and if * we have nothing past the link-layer header, * print nothing. */ if (h->caplen > hdrlen) ascii_print(sp + hdrlen, h->caplen - hdrlen); } } putchar('\n'); --infodelay; if (infoprint) info(0); } #ifdef WIN32 /* * XXX - there should really be libpcap calls to get the version * number as a string (the string would be generated from #defines * at run time, so that it's not generated from string constants * in the library, as, on many UNIX systems, those constants would * be statically linked into the application executable image, and * would thus reflect the version of libpcap on the system on * which the application was *linked*, not the system on which it's * *running*. * * That routine should be documented, unlike the "version[]" * string, so that UNIX vendors providing their own libpcaps * don't omit it (as a couple of vendors have...). * * Packet.dll should perhaps also export a routine to return the * version number of the Packet.dll code, to supply the * "Wpcap_version" information on Windows. */ char WDversion[]="current-cvs.tcpdump.org"; #if !defined(HAVE_GENERATED_VERSION) char version[]="current-cvs.tcpdump.org"; #endif char pcap_version[]="current-cvs.tcpdump.org"; char Wpcap_version[]="3.1"; #endif /* * By default, print the specified data out in hex and ASCII. */ static void ndo_default_print(netdissect_options *ndo _U_, const u_char *bp, u_int length) { hex_and_ascii_print("\n\t", bp, length); /* pass on lf and identation string */ } void default_print(const u_char *bp, u_int length) { ndo_default_print(gndo, bp, length); } #ifdef SIGNAL_REQ_INFO RETSIGTYPE requestinfo(int signo _U_) { if (infodelay) ++infoprint; else info(0); } #endif /* * Called once each second in verbose mode while dumping to file */ #ifdef USE_WIN32_MM_TIMER void CALLBACK verbose_stats_dump (UINT timer_id _U_, UINT msg _U_, DWORD_PTR arg _U_, DWORD_PTR dw1 _U_, DWORD_PTR dw2 _U_) { struct pcap_stat stat; if (infodelay == 0 && pcap_stats(pd, &stat) >= 0) fprintf(stderr, "Got %u\r", packets_captured); } #elif defined(HAVE_ALARM) static void verbose_stats_dump(int sig _U_) { struct pcap_stat stat; if (infodelay == 0 && pcap_stats(pd, &stat) >= 0) fprintf(stderr, "Got %u\r", packets_captured); alarm(1); } #endif static void usage(void) { extern char version[]; #ifndef HAVE_PCAP_LIB_VERSION #if defined(WIN32) || defined(HAVE_PCAP_VERSION) extern char pcap_version[]; #else /* defined(WIN32) || defined(HAVE_PCAP_VERSION) */ static char pcap_version[] = "unknown"; #endif /* defined(WIN32) || defined(HAVE_PCAP_VERSION) */ #endif /* HAVE_PCAP_LIB_VERSION */ #ifdef HAVE_PCAP_LIB_VERSION #ifdef WIN32 (void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version); #else /* WIN32 */ (void)fprintf(stderr, "%s version %s\n", program_name, version); #endif /* WIN32 */ (void)fprintf(stderr, "%s\n",pcap_lib_version()); #else /* HAVE_PCAP_LIB_VERSION */ #ifdef WIN32 (void)fprintf(stderr, "%s version %s, based on tcpdump version %s\n", program_name, WDversion, version); (void)fprintf(stderr, "WinPcap version %s, based on libpcap version %s\n",Wpcap_version, pcap_version); #else /* WIN32 */ (void)fprintf(stderr, "%s version %s\n", program_name, version); (void)fprintf(stderr, "libpcap version %s\n", pcap_version); #endif /* WIN32 */ #endif /* HAVE_PCAP_LIB_VERSION */ (void)fprintf(stderr, "Usage: %s [-aAbd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqRStu" U_FLAG "vxX]" B_FLAG_USAGE " [ -c count ]\n", program_name); (void)fprintf(stderr, "\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n"); (void)fprintf(stderr, "\t\t[ -i interface ]" j_FLAG_USAGE " [ -M secret ]\n"); (void)fprintf(stderr, "\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ -V file ] [ -w file ]\n"); (void)fprintf(stderr, "\t\t[ -W filecount ] [ -y datalinktype ] [ -z command ]\n"); (void)fprintf(stderr, "\t\t[ -Z user ] [ expression ]\n"); exit(1); } /* VARARGS */ static void ndo_error(netdissect_options *ndo _U_, const char *fmt, ...) { va_list ap; (void)fprintf(stderr, "%s: ", program_name); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } exit(1); /* NOTREACHED */ } /* VARARGS */ static void ndo_warning(netdissect_options *ndo _U_, const char *fmt, ...) { va_list ap; (void)fprintf(stderr, "%s: WARNING: ", program_name); va_start(ap, fmt); (void)vfprintf(stderr, fmt, ap); va_end(ap); if (*fmt) { fmt += strlen(fmt); if (fmt[-1] != '\n') (void)fputc('\n', stderr); } } Index: stable/10/crypto/openssh/sandbox-capsicum.c =================================================================== --- stable/10/crypto/openssh/sandbox-capsicum.c (revision 280249) +++ stable/10/crypto/openssh/sandbox-capsicum.c (revision 280250) @@ -1,123 +1,123 @@ /* * Copyright (c) 2011 Dag-Erling Smorgrav * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" __RCSID("$FreeBSD$"); #ifdef SANDBOX_CAPSICUM #include #include #include #include -#include +#include #include #include #include #include #include #include #include "log.h" #include "monitor.h" #include "ssh-sandbox.h" #include "xmalloc.h" /* * Capsicum sandbox that sets zero nfiles, nprocs and filesize rlimits, * limits rights on stdout, stdin, stderr, monitor and switches to * capability mode. */ struct ssh_sandbox { struct monitor *monitor; pid_t child_pid; }; struct ssh_sandbox * ssh_sandbox_init(struct monitor *monitor) { struct ssh_sandbox *box; /* * Strictly, we don't need to maintain any state here but we need * to return non-NULL to satisfy the API. */ debug3("%s: preparing capsicum sandbox", __func__); box = xcalloc(1, sizeof(*box)); box->monitor = monitor; box->child_pid = 0; return box; } void ssh_sandbox_child(struct ssh_sandbox *box) { struct rlimit rl_zero; cap_rights_t rights; rl_zero.rlim_cur = rl_zero.rlim_max = 0; if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", __func__, strerror(errno)); #ifndef SANDBOX_SKIP_RLIMIT_NOFILE if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", __func__, strerror(errno)); #endif if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", __func__, strerror(errno)); cap_rights_init(&rights); if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS) fatal("can't limit stdin: %m"); if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS) fatal("can't limit stdout: %m"); if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS) fatal("can't limit stderr: %m"); cap_rights_init(&rights, CAP_READ, CAP_WRITE); if (cap_rights_limit(box->monitor->m_recvfd, &rights) < 0 && errno != ENOSYS) fatal("%s: failed to limit the network socket", __func__); cap_rights_init(&rights, CAP_WRITE); if (cap_rights_limit(box->monitor->m_log_sendfd, &rights) < 0 && errno != ENOSYS) fatal("%s: failed to limit the logging socket", __func__); if (cap_enter() < 0 && errno != ENOSYS) fatal("%s: failed to enter capability mode", __func__); } void ssh_sandbox_parent_finish(struct ssh_sandbox *box) { free(box); debug3("%s: finished", __func__); } void ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) { box->child_pid = child_pid; } #endif /* SANDBOX_CAPSICUM */ Index: stable/10/lib/libc/gen/cap_sandboxed.c =================================================================== --- stable/10/lib/libc/gen/cap_sandboxed.c (revision 280249) +++ stable/10/lib/libc/gen/cap_sandboxed.c (revision 280250) @@ -1,50 +1,50 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); -#include +#include #include #include #include bool cap_sandboxed(void) { u_int mode; if (cap_getmode(&mode) != 0) { assert(errno == ENOSYS); return (false); } assert(mode == 0 || mode == 1); return (mode == 1); } Index: stable/10/lib/libprocstat/libprocstat.c =================================================================== --- stable/10/lib/libprocstat/libprocstat.c (revision 280249) +++ stable/10/lib/libprocstat/libprocstat.c (revision 280250) @@ -1,2529 +1,2529 @@ /*- * Copyright (c) 2009 Stanislav Sedov * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #define _WANT_UCRED #include #undef _WANT_UCRED #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _WANT_FILE #include #include #include #include -#include +#include #define _KERNEL #include #include #include #include #include #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libprocstat_internal.h" #include "common_kvm.h" #include "core.h" int statfs(const char *, struct statfs *); /* XXX */ #define PROCSTAT_KVM 1 #define PROCSTAT_SYSCTL 2 #define PROCSTAT_CORE 3 static char **getargv(struct procstat *procstat, struct kinfo_proc *kp, size_t nchr, int env); static char *getmnton(kvm_t *kd, struct mount *m); static struct kinfo_vmentry * kinfo_getvmmap_core(struct procstat_core *core, int *cntp); static Elf_Auxinfo *procstat_getauxv_core(struct procstat_core *core, unsigned int *cntp); static Elf_Auxinfo *procstat_getauxv_sysctl(pid_t pid, unsigned int *cntp); static struct filestat_list *procstat_getfiles_kvm( struct procstat *procstat, struct kinfo_proc *kp, int mmapped); static struct filestat_list *procstat_getfiles_sysctl( struct procstat *procstat, struct kinfo_proc *kp, int mmapped); static int procstat_get_pipe_info_sysctl(struct filestat *fst, struct pipestat *pipe, char *errbuf); static int procstat_get_pipe_info_kvm(kvm_t *kd, struct filestat *fst, struct pipestat *pipe, char *errbuf); static int procstat_get_pts_info_sysctl(struct filestat *fst, struct ptsstat *pts, char *errbuf); static int procstat_get_pts_info_kvm(kvm_t *kd, struct filestat *fst, struct ptsstat *pts, char *errbuf); static int procstat_get_sem_info_sysctl(struct filestat *fst, struct semstat *sem, char *errbuf); static int procstat_get_sem_info_kvm(kvm_t *kd, struct filestat *fst, struct semstat *sem, char *errbuf); static int procstat_get_shm_info_sysctl(struct filestat *fst, struct shmstat *shm, char *errbuf); static int procstat_get_shm_info_kvm(kvm_t *kd, struct filestat *fst, struct shmstat *shm, char *errbuf); static int procstat_get_socket_info_sysctl(struct filestat *fst, struct sockstat *sock, char *errbuf); static int procstat_get_socket_info_kvm(kvm_t *kd, struct filestat *fst, struct sockstat *sock, char *errbuf); static int to_filestat_flags(int flags); static int procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, struct vnstat *vn, char *errbuf); static int procstat_get_vnode_info_sysctl(struct filestat *fst, struct vnstat *vn, char *errbuf); static gid_t *procstat_getgroups_core(struct procstat_core *core, unsigned int *count); static gid_t * procstat_getgroups_kvm(kvm_t *kd, struct kinfo_proc *kp, unsigned int *count); static gid_t *procstat_getgroups_sysctl(pid_t pid, unsigned int *count); static struct kinfo_kstack *procstat_getkstack_sysctl(pid_t pid, int *cntp); static int procstat_getosrel_core(struct procstat_core *core, int *osrelp); static int procstat_getosrel_kvm(kvm_t *kd, struct kinfo_proc *kp, int *osrelp); static int procstat_getosrel_sysctl(pid_t pid, int *osrelp); static int procstat_getpathname_core(struct procstat_core *core, char *pathname, size_t maxlen); static int procstat_getpathname_sysctl(pid_t pid, char *pathname, size_t maxlen); static int procstat_getrlimit_core(struct procstat_core *core, int which, struct rlimit* rlimit); static int procstat_getrlimit_kvm(kvm_t *kd, struct kinfo_proc *kp, int which, struct rlimit* rlimit); static int procstat_getrlimit_sysctl(pid_t pid, int which, struct rlimit* rlimit); static int procstat_getumask_core(struct procstat_core *core, unsigned short *maskp); static int procstat_getumask_kvm(kvm_t *kd, struct kinfo_proc *kp, unsigned short *maskp); static int procstat_getumask_sysctl(pid_t pid, unsigned short *maskp); static int vntype2psfsttype(int type); void procstat_close(struct procstat *procstat) { assert(procstat); if (procstat->type == PROCSTAT_KVM) kvm_close(procstat->kd); else if (procstat->type == PROCSTAT_CORE) procstat_core_close(procstat->core); procstat_freeargv(procstat); procstat_freeenvv(procstat); free(procstat); } struct procstat * procstat_open_sysctl(void) { struct procstat *procstat; procstat = calloc(1, sizeof(*procstat)); if (procstat == NULL) { warn("malloc()"); return (NULL); } procstat->type = PROCSTAT_SYSCTL; return (procstat); } struct procstat * procstat_open_kvm(const char *nlistf, const char *memf) { struct procstat *procstat; kvm_t *kd; char buf[_POSIX2_LINE_MAX]; procstat = calloc(1, sizeof(*procstat)); if (procstat == NULL) { warn("malloc()"); return (NULL); } kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf); if (kd == NULL) { warnx("kvm_openfiles(): %s", buf); free(procstat); return (NULL); } procstat->type = PROCSTAT_KVM; procstat->kd = kd; return (procstat); } struct procstat * procstat_open_core(const char *filename) { struct procstat *procstat; struct procstat_core *core; procstat = calloc(1, sizeof(*procstat)); if (procstat == NULL) { warn("malloc()"); return (NULL); } core = procstat_core_open(filename); if (core == NULL) { free(procstat); return (NULL); } procstat->type = PROCSTAT_CORE; procstat->core = core; return (procstat); } struct kinfo_proc * procstat_getprocs(struct procstat *procstat, int what, int arg, unsigned int *count) { struct kinfo_proc *p0, *p; size_t len, olen; int name[4]; int cnt; int error; assert(procstat); assert(count); p = NULL; if (procstat->type == PROCSTAT_KVM) { *count = 0; p0 = kvm_getprocs(procstat->kd, what, arg, &cnt); if (p0 == NULL || cnt <= 0) return (NULL); *count = cnt; len = *count * sizeof(*p); p = malloc(len); if (p == NULL) { warnx("malloc(%zu)", len); goto fail; } bcopy(p0, p, len); return (p); } else if (procstat->type == PROCSTAT_SYSCTL) { len = 0; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = what; name[3] = arg; error = sysctl(name, 4, NULL, &len, NULL, 0); if (error < 0 && errno != EPERM) { warn("sysctl(kern.proc)"); goto fail; } if (len == 0) { warnx("no processes?"); goto fail; } do { len += len / 10; p = reallocf(p, len); if (p == NULL) { warnx("reallocf(%zu)", len); goto fail; } olen = len; error = sysctl(name, 4, p, &len, NULL, 0); } while (error < 0 && errno == ENOMEM && olen == len); if (error < 0 && errno != EPERM) { warn("sysctl(kern.proc)"); goto fail; } /* Perform simple consistency checks. */ if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { warnx("kinfo_proc structure size mismatch (len = %zu)", len); goto fail; } *count = len / sizeof(*p); return (p); } else if (procstat->type == PROCSTAT_CORE) { p = procstat_core_get(procstat->core, PSC_TYPE_PROC, NULL, &len); if ((len % sizeof(*p)) != 0 || p->ki_structsize != sizeof(*p)) { warnx("kinfo_proc structure size mismatch"); goto fail; } *count = len / sizeof(*p); return (p); } else { warnx("unknown access method: %d", procstat->type); return (NULL); } fail: if (p) free(p); return (NULL); } void procstat_freeprocs(struct procstat *procstat __unused, struct kinfo_proc *p) { if (p != NULL) free(p); p = NULL; } struct filestat_list * procstat_getfiles(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) { switch(procstat->type) { case PROCSTAT_KVM: return (procstat_getfiles_kvm(procstat, kp, mmapped)); case PROCSTAT_SYSCTL: case PROCSTAT_CORE: return (procstat_getfiles_sysctl(procstat, kp, mmapped)); default: warnx("unknown access method: %d", procstat->type); return (NULL); } } void procstat_freefiles(struct procstat *procstat, struct filestat_list *head) { struct filestat *fst, *tmp; STAILQ_FOREACH_SAFE(fst, head, next, tmp) { if (fst->fs_path != NULL) free(fst->fs_path); free(fst); } free(head); if (procstat->vmentries != NULL) { free(procstat->vmentries); procstat->vmentries = NULL; } if (procstat->files != NULL) { free(procstat->files); procstat->files = NULL; } } static struct filestat * filestat_new_entry(void *typedep, int type, int fd, int fflags, int uflags, int refcount, off_t offset, char *path, cap_rights_t *cap_rightsp) { struct filestat *entry; entry = calloc(1, sizeof(*entry)); if (entry == NULL) { warn("malloc()"); return (NULL); } entry->fs_typedep = typedep; entry->fs_fflags = fflags; entry->fs_uflags = uflags; entry->fs_fd = fd; entry->fs_type = type; entry->fs_ref_count = refcount; entry->fs_offset = offset; entry->fs_path = path; if (cap_rightsp != NULL) entry->fs_cap_rights = *cap_rightsp; else cap_rights_init(&entry->fs_cap_rights); return (entry); } static struct vnode * getctty(kvm_t *kd, struct kinfo_proc *kp) { struct pgrp pgrp; struct proc proc; struct session sess; int error; assert(kp); error = kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc, sizeof(proc)); if (error == 0) { warnx("can't read proc struct at %p for pid %d", kp->ki_paddr, kp->ki_pid); return (NULL); } if (proc.p_pgrp == NULL) return (NULL); error = kvm_read_all(kd, (unsigned long)proc.p_pgrp, &pgrp, sizeof(pgrp)); if (error == 0) { warnx("can't read pgrp struct at %p for pid %d", proc.p_pgrp, kp->ki_pid); return (NULL); } error = kvm_read_all(kd, (unsigned long)pgrp.pg_session, &sess, sizeof(sess)); if (error == 0) { warnx("can't read session struct at %p for pid %d", pgrp.pg_session, kp->ki_pid); return (NULL); } return (sess.s_ttyvp); } static struct filestat_list * procstat_getfiles_kvm(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) { struct file file; struct filedesc filed; struct vm_map_entry vmentry; struct vm_object object; struct vmspace vmspace; vm_map_entry_t entryp; vm_map_t map; vm_object_t objp; struct vnode *vp; struct file **ofiles; struct filestat *entry; struct filestat_list *head; kvm_t *kd; void *data; int i, fflags; int prot, type; unsigned int nfiles; assert(procstat); kd = procstat->kd; if (kd == NULL) return (NULL); if (kp->ki_fd == NULL) return (NULL); if (!kvm_read_all(kd, (unsigned long)kp->ki_fd, &filed, sizeof(filed))) { warnx("can't read filedesc at %p", (void *)kp->ki_fd); return (NULL); } /* * Allocate list head. */ head = malloc(sizeof(*head)); if (head == NULL) return (NULL); STAILQ_INIT(head); /* root directory vnode, if one. */ if (filed.fd_rdir) { entry = filestat_new_entry(filed.fd_rdir, PS_FST_TYPE_VNODE, -1, PS_FST_FFLAG_READ, PS_FST_UFLAG_RDIR, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* current working directory vnode. */ if (filed.fd_cdir) { entry = filestat_new_entry(filed.fd_cdir, PS_FST_TYPE_VNODE, -1, PS_FST_FFLAG_READ, PS_FST_UFLAG_CDIR, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* jail root, if any. */ if (filed.fd_jdir) { entry = filestat_new_entry(filed.fd_jdir, PS_FST_TYPE_VNODE, -1, PS_FST_FFLAG_READ, PS_FST_UFLAG_JAIL, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* ktrace vnode, if one */ if (kp->ki_tracep) { entry = filestat_new_entry(kp->ki_tracep, PS_FST_TYPE_VNODE, -1, PS_FST_FFLAG_READ | PS_FST_FFLAG_WRITE, PS_FST_UFLAG_TRACE, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* text vnode, if one */ if (kp->ki_textvp) { entry = filestat_new_entry(kp->ki_textvp, PS_FST_TYPE_VNODE, -1, PS_FST_FFLAG_READ, PS_FST_UFLAG_TEXT, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } /* Controlling terminal. */ if ((vp = getctty(kd, kp)) != NULL) { entry = filestat_new_entry(vp, PS_FST_TYPE_VNODE, -1, PS_FST_FFLAG_READ | PS_FST_FFLAG_WRITE, PS_FST_UFLAG_CTTY, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } nfiles = filed.fd_lastfile + 1; ofiles = malloc(nfiles * sizeof(struct file *)); if (ofiles == NULL) { warn("malloc(%zu)", nfiles * sizeof(struct file *)); goto do_mmapped; } if (!kvm_read_all(kd, (unsigned long)filed.fd_ofiles, ofiles, nfiles * sizeof(struct file *))) { warnx("cannot read file structures at %p", (void *)filed.fd_ofiles); free(ofiles); goto do_mmapped; } for (i = 0; i <= filed.fd_lastfile; i++) { if (ofiles[i] == NULL) continue; if (!kvm_read_all(kd, (unsigned long)ofiles[i], &file, sizeof(struct file))) { warnx("can't read file %d at %p", i, (void *)ofiles[i]); continue; } switch (file.f_type) { case DTYPE_VNODE: type = PS_FST_TYPE_VNODE; data = file.f_vnode; break; case DTYPE_SOCKET: type = PS_FST_TYPE_SOCKET; data = file.f_data; break; case DTYPE_PIPE: type = PS_FST_TYPE_PIPE; data = file.f_data; break; case DTYPE_FIFO: type = PS_FST_TYPE_FIFO; data = file.f_vnode; break; #ifdef DTYPE_PTS case DTYPE_PTS: type = PS_FST_TYPE_PTS; data = file.f_data; break; #endif case DTYPE_SEM: type = PS_FST_TYPE_SEM; data = file.f_data; break; case DTYPE_SHM: type = PS_FST_TYPE_SHM; data = file.f_data; break; default: continue; } /* XXXRW: No capability rights support for kvm yet. */ entry = filestat_new_entry(data, type, i, to_filestat_flags(file.f_flag), 0, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } free(ofiles); do_mmapped: /* * Process mmapped files if requested. */ if (mmapped) { if (!kvm_read_all(kd, (unsigned long)kp->ki_vmspace, &vmspace, sizeof(vmspace))) { warnx("can't read vmspace at %p", (void *)kp->ki_vmspace); goto exit; } map = &vmspace.vm_map; for (entryp = map->header.next; entryp != &kp->ki_vmspace->vm_map.header; entryp = vmentry.next) { if (!kvm_read_all(kd, (unsigned long)entryp, &vmentry, sizeof(vmentry))) { warnx("can't read vm_map_entry at %p", (void *)entryp); continue; } if (vmentry.eflags & MAP_ENTRY_IS_SUB_MAP) continue; if ((objp = vmentry.object.vm_object) == NULL) continue; for (; objp; objp = object.backing_object) { if (!kvm_read_all(kd, (unsigned long)objp, &object, sizeof(object))) { warnx("can't read vm_object at %p", (void *)objp); break; } } /* We want only vnode objects. */ if (object.type != OBJT_VNODE) continue; prot = vmentry.protection; fflags = 0; if (prot & VM_PROT_READ) fflags = PS_FST_FFLAG_READ; if ((vmentry.eflags & MAP_ENTRY_COW) == 0 && prot & VM_PROT_WRITE) fflags |= PS_FST_FFLAG_WRITE; /* * Create filestat entry. */ entry = filestat_new_entry(object.handle, PS_FST_TYPE_VNODE, -1, fflags, PS_FST_UFLAG_MMAP, 0, 0, NULL, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } } exit: return (head); } /* * kinfo types to filestat translation. */ static int kinfo_type2fst(int kftype) { static struct { int kf_type; int fst_type; } kftypes2fst[] = { { KF_TYPE_CRYPTO, PS_FST_TYPE_CRYPTO }, { KF_TYPE_FIFO, PS_FST_TYPE_FIFO }, { KF_TYPE_KQUEUE, PS_FST_TYPE_KQUEUE }, { KF_TYPE_MQUEUE, PS_FST_TYPE_MQUEUE }, { KF_TYPE_NONE, PS_FST_TYPE_NONE }, { KF_TYPE_PIPE, PS_FST_TYPE_PIPE }, { KF_TYPE_PTS, PS_FST_TYPE_PTS }, { KF_TYPE_SEM, PS_FST_TYPE_SEM }, { KF_TYPE_SHM, PS_FST_TYPE_SHM }, { KF_TYPE_SOCKET, PS_FST_TYPE_SOCKET }, { KF_TYPE_VNODE, PS_FST_TYPE_VNODE }, { KF_TYPE_UNKNOWN, PS_FST_TYPE_UNKNOWN } }; #define NKFTYPES (sizeof(kftypes2fst) / sizeof(*kftypes2fst)) unsigned int i; for (i = 0; i < NKFTYPES; i++) if (kftypes2fst[i].kf_type == kftype) break; if (i == NKFTYPES) return (PS_FST_TYPE_UNKNOWN); return (kftypes2fst[i].fst_type); } /* * kinfo flags to filestat translation. */ static int kinfo_fflags2fst(int kfflags) { static struct { int kf_flag; int fst_flag; } kfflags2fst[] = { { KF_FLAG_APPEND, PS_FST_FFLAG_APPEND }, { KF_FLAG_ASYNC, PS_FST_FFLAG_ASYNC }, { KF_FLAG_CREAT, PS_FST_FFLAG_CREAT }, { KF_FLAG_DIRECT, PS_FST_FFLAG_DIRECT }, { KF_FLAG_EXCL, PS_FST_FFLAG_EXCL }, { KF_FLAG_EXEC, PS_FST_FFLAG_EXEC }, { KF_FLAG_EXLOCK, PS_FST_FFLAG_EXLOCK }, { KF_FLAG_FSYNC, PS_FST_FFLAG_SYNC }, { KF_FLAG_HASLOCK, PS_FST_FFLAG_HASLOCK }, { KF_FLAG_NOFOLLOW, PS_FST_FFLAG_NOFOLLOW }, { KF_FLAG_NONBLOCK, PS_FST_FFLAG_NONBLOCK }, { KF_FLAG_READ, PS_FST_FFLAG_READ }, { KF_FLAG_SHLOCK, PS_FST_FFLAG_SHLOCK }, { KF_FLAG_TRUNC, PS_FST_FFLAG_TRUNC }, { KF_FLAG_WRITE, PS_FST_FFLAG_WRITE } }; #define NKFFLAGS (sizeof(kfflags2fst) / sizeof(*kfflags2fst)) unsigned int i; int flags; flags = 0; for (i = 0; i < NKFFLAGS; i++) if ((kfflags & kfflags2fst[i].kf_flag) != 0) flags |= kfflags2fst[i].fst_flag; return (flags); } static int kinfo_uflags2fst(int fd) { switch (fd) { case KF_FD_TYPE_CTTY: return (PS_FST_UFLAG_CTTY); case KF_FD_TYPE_CWD: return (PS_FST_UFLAG_CDIR); case KF_FD_TYPE_JAIL: return (PS_FST_UFLAG_JAIL); case KF_FD_TYPE_TEXT: return (PS_FST_UFLAG_TEXT); case KF_FD_TYPE_TRACE: return (PS_FST_UFLAG_TRACE); case KF_FD_TYPE_ROOT: return (PS_FST_UFLAG_RDIR); } return (0); } static struct kinfo_file * kinfo_getfile_core(struct procstat_core *core, int *cntp) { int cnt; size_t len; char *buf, *bp, *eb; struct kinfo_file *kif, *kp, *kf; buf = procstat_core_get(core, PSC_TYPE_FILES, NULL, &len); if (buf == NULL) return (NULL); /* * XXXMG: The code below is just copy&past from libutil. * The code duplication can be avoided if libutil * is extended to provide something like: * struct kinfo_file *kinfo_getfile_from_buf(const char *buf, * size_t len, int *cntp); */ /* Pass 1: count items */ cnt = 0; bp = buf; eb = buf + len; while (bp < eb) { kf = (struct kinfo_file *)(uintptr_t)bp; bp += kf->kf_structsize; cnt++; } kif = calloc(cnt, sizeof(*kif)); if (kif == NULL) { free(buf); return (NULL); } bp = buf; eb = buf + len; kp = kif; /* Pass 2: unpack */ while (bp < eb) { kf = (struct kinfo_file *)(uintptr_t)bp; /* Copy/expand into pre-zeroed buffer */ memcpy(kp, kf, kf->kf_structsize); /* Advance to next packed record */ bp += kf->kf_structsize; /* Set field size to fixed length, advance */ kp->kf_structsize = sizeof(*kp); kp++; } free(buf); *cntp = cnt; return (kif); /* Caller must free() return value */ } static struct filestat_list * procstat_getfiles_sysctl(struct procstat *procstat, struct kinfo_proc *kp, int mmapped) { struct kinfo_file *kif, *files; struct kinfo_vmentry *kve, *vmentries; struct filestat_list *head; struct filestat *entry; char *path; off_t offset; int cnt, fd, fflags; int i, type, uflags; int refcount; cap_rights_t cap_rights; assert(kp); if (kp->ki_fd == NULL) return (NULL); switch(procstat->type) { case PROCSTAT_SYSCTL: files = kinfo_getfile(kp->ki_pid, &cnt); break; case PROCSTAT_CORE: files = kinfo_getfile_core(procstat->core, &cnt); break; default: assert(!"invalid type"); } if (files == NULL && errno != EPERM) { warn("kinfo_getfile()"); return (NULL); } procstat->files = files; /* * Allocate list head. */ head = malloc(sizeof(*head)); if (head == NULL) return (NULL); STAILQ_INIT(head); for (i = 0; i < cnt; i++) { kif = &files[i]; type = kinfo_type2fst(kif->kf_type); fd = kif->kf_fd >= 0 ? kif->kf_fd : -1; fflags = kinfo_fflags2fst(kif->kf_flags); uflags = kinfo_uflags2fst(kif->kf_fd); refcount = kif->kf_ref_count; offset = kif->kf_offset; if (*kif->kf_path != '\0') path = strdup(kif->kf_path); else path = NULL; cap_rights = kif->kf_cap_rights; /* * Create filestat entry. */ entry = filestat_new_entry(kif, type, fd, fflags, uflags, refcount, offset, path, &cap_rights); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } if (mmapped != 0) { vmentries = procstat_getvmmap(procstat, kp, &cnt); procstat->vmentries = vmentries; if (vmentries == NULL || cnt == 0) goto fail; for (i = 0; i < cnt; i++) { kve = &vmentries[i]; if (kve->kve_type != KVME_TYPE_VNODE) continue; fflags = 0; if (kve->kve_protection & KVME_PROT_READ) fflags = PS_FST_FFLAG_READ; if ((kve->kve_flags & KVME_FLAG_COW) == 0 && kve->kve_protection & KVME_PROT_WRITE) fflags |= PS_FST_FFLAG_WRITE; offset = kve->kve_offset; refcount = kve->kve_ref_count; if (*kve->kve_path != '\0') path = strdup(kve->kve_path); else path = NULL; entry = filestat_new_entry(kve, PS_FST_TYPE_VNODE, -1, fflags, PS_FST_UFLAG_MMAP, refcount, offset, path, NULL); if (entry != NULL) STAILQ_INSERT_TAIL(head, entry, next); } } fail: return (head); } int procstat_get_pipe_info(struct procstat *procstat, struct filestat *fst, struct pipestat *ps, char *errbuf) { assert(ps); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pipe_info_kvm(procstat->kd, fst, ps, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL || procstat->type == PROCSTAT_CORE) { return (procstat_get_pipe_info_sysctl(fst, ps, errbuf)); } else { warnx("unknown access method: %d", procstat->type); if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } static int procstat_get_pipe_info_kvm(kvm_t *kd, struct filestat *fst, struct pipestat *ps, char *errbuf) { struct pipe pi; void *pipep; assert(kd); assert(ps); assert(fst); bzero(ps, sizeof(*ps)); pipep = fst->fs_typedep; if (pipep == NULL) goto fail; if (!kvm_read_all(kd, (unsigned long)pipep, &pi, sizeof(struct pipe))) { warnx("can't read pipe at %p", (void *)pipep); goto fail; } ps->addr = (uintptr_t)pipep; ps->peer = (uintptr_t)pi.pipe_peer; ps->buffer_cnt = pi.pipe_buffer.cnt; return (0); fail: if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } static int procstat_get_pipe_info_sysctl(struct filestat *fst, struct pipestat *ps, char *errbuf __unused) { struct kinfo_file *kif; assert(ps); assert(fst); bzero(ps, sizeof(*ps)); kif = fst->fs_typedep; if (kif == NULL) return (1); ps->addr = kif->kf_un.kf_pipe.kf_pipe_addr; ps->peer = kif->kf_un.kf_pipe.kf_pipe_peer; ps->buffer_cnt = kif->kf_un.kf_pipe.kf_pipe_buffer_cnt; return (0); } int procstat_get_pts_info(struct procstat *procstat, struct filestat *fst, struct ptsstat *pts, char *errbuf) { assert(pts); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_pts_info_kvm(procstat->kd, fst, pts, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL || procstat->type == PROCSTAT_CORE) { return (procstat_get_pts_info_sysctl(fst, pts, errbuf)); } else { warnx("unknown access method: %d", procstat->type); if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } static int procstat_get_pts_info_kvm(kvm_t *kd, struct filestat *fst, struct ptsstat *pts, char *errbuf) { struct tty tty; void *ttyp; assert(kd); assert(pts); assert(fst); bzero(pts, sizeof(*pts)); ttyp = fst->fs_typedep; if (ttyp == NULL) goto fail; if (!kvm_read_all(kd, (unsigned long)ttyp, &tty, sizeof(struct tty))) { warnx("can't read tty at %p", (void *)ttyp); goto fail; } pts->dev = dev2udev(kd, tty.t_dev); (void)kdevtoname(kd, tty.t_dev, pts->devname); return (0); fail: if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } static int procstat_get_pts_info_sysctl(struct filestat *fst, struct ptsstat *pts, char *errbuf __unused) { struct kinfo_file *kif; assert(pts); assert(fst); bzero(pts, sizeof(*pts)); kif = fst->fs_typedep; if (kif == NULL) return (0); pts->dev = kif->kf_un.kf_pts.kf_pts_dev; strlcpy(pts->devname, kif->kf_path, sizeof(pts->devname)); return (0); } int procstat_get_sem_info(struct procstat *procstat, struct filestat *fst, struct semstat *sem, char *errbuf) { assert(sem); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_sem_info_kvm(procstat->kd, fst, sem, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL || procstat->type == PROCSTAT_CORE) { return (procstat_get_sem_info_sysctl(fst, sem, errbuf)); } else { warnx("unknown access method: %d", procstat->type); if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } static int procstat_get_sem_info_kvm(kvm_t *kd, struct filestat *fst, struct semstat *sem, char *errbuf) { struct ksem ksem; void *ksemp; char *path; int i; assert(kd); assert(sem); assert(fst); bzero(sem, sizeof(*sem)); ksemp = fst->fs_typedep; if (ksemp == NULL) goto fail; if (!kvm_read_all(kd, (unsigned long)ksemp, &ksem, sizeof(struct ksem))) { warnx("can't read ksem at %p", (void *)ksemp); goto fail; } sem->mode = S_IFREG | ksem.ks_mode; sem->value = ksem.ks_value; if (fst->fs_path == NULL && ksem.ks_path != NULL) { path = malloc(MAXPATHLEN); for (i = 0; i < MAXPATHLEN - 1; i++) { if (!kvm_read_all(kd, (unsigned long)ksem.ks_path + i, path + i, 1)) break; if (path[i] == '\0') break; } path[i] = '\0'; if (i == 0) free(path); else fst->fs_path = path; } return (0); fail: if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } static int procstat_get_sem_info_sysctl(struct filestat *fst, struct semstat *sem, char *errbuf __unused) { struct kinfo_file *kif; assert(sem); assert(fst); bzero(sem, sizeof(*sem)); kif = fst->fs_typedep; if (kif == NULL) return (0); sem->value = kif->kf_un.kf_sem.kf_sem_value; sem->mode = kif->kf_un.kf_sem.kf_sem_mode; return (0); } int procstat_get_shm_info(struct procstat *procstat, struct filestat *fst, struct shmstat *shm, char *errbuf) { assert(shm); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_shm_info_kvm(procstat->kd, fst, shm, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL || procstat->type == PROCSTAT_CORE) { return (procstat_get_shm_info_sysctl(fst, shm, errbuf)); } else { warnx("unknown access method: %d", procstat->type); if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } static int procstat_get_shm_info_kvm(kvm_t *kd, struct filestat *fst, struct shmstat *shm, char *errbuf) { struct shmfd shmfd; void *shmfdp; char *path; int i; assert(kd); assert(shm); assert(fst); bzero(shm, sizeof(*shm)); shmfdp = fst->fs_typedep; if (shmfdp == NULL) goto fail; if (!kvm_read_all(kd, (unsigned long)shmfdp, &shmfd, sizeof(struct shmfd))) { warnx("can't read shmfd at %p", (void *)shmfdp); goto fail; } shm->mode = S_IFREG | shmfd.shm_mode; shm->size = shmfd.shm_size; if (fst->fs_path == NULL && shmfd.shm_path != NULL) { path = malloc(MAXPATHLEN); for (i = 0; i < MAXPATHLEN - 1; i++) { if (!kvm_read_all(kd, (unsigned long)shmfd.shm_path + i, path + i, 1)) break; if (path[i] == '\0') break; } path[i] = '\0'; if (i == 0) free(path); else fst->fs_path = path; } return (0); fail: if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } static int procstat_get_shm_info_sysctl(struct filestat *fst, struct shmstat *shm, char *errbuf __unused) { struct kinfo_file *kif; assert(shm); assert(fst); bzero(shm, sizeof(*shm)); kif = fst->fs_typedep; if (kif == NULL) return (0); shm->size = kif->kf_un.kf_file.kf_file_size; shm->mode = kif->kf_un.kf_file.kf_file_mode; return (0); } int procstat_get_vnode_info(struct procstat *procstat, struct filestat *fst, struct vnstat *vn, char *errbuf) { assert(vn); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_vnode_info_kvm(procstat->kd, fst, vn, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL || procstat->type == PROCSTAT_CORE) { return (procstat_get_vnode_info_sysctl(fst, vn, errbuf)); } else { warnx("unknown access method: %d", procstat->type); if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } static int procstat_get_vnode_info_kvm(kvm_t *kd, struct filestat *fst, struct vnstat *vn, char *errbuf) { /* Filesystem specific handlers. */ #define FSTYPE(fst) {#fst, fst##_filestat} struct { const char *tag; int (*handler)(kvm_t *kd, struct vnode *vp, struct vnstat *vn); } fstypes[] = { FSTYPE(devfs), FSTYPE(isofs), FSTYPE(msdosfs), FSTYPE(nfs), FSTYPE(smbfs), FSTYPE(udf), FSTYPE(ufs), #ifdef LIBPROCSTAT_ZFS FSTYPE(zfs), #endif }; #define NTYPES (sizeof(fstypes) / sizeof(*fstypes)) struct vnode vnode; char tagstr[12]; void *vp; int error, found; unsigned int i; assert(kd); assert(vn); assert(fst); vp = fst->fs_typedep; if (vp == NULL) goto fail; error = kvm_read_all(kd, (unsigned long)vp, &vnode, sizeof(vnode)); if (error == 0) { warnx("can't read vnode at %p", (void *)vp); goto fail; } bzero(vn, sizeof(*vn)); vn->vn_type = vntype2psfsttype(vnode.v_type); if (vnode.v_type == VNON || vnode.v_type == VBAD) return (0); error = kvm_read_all(kd, (unsigned long)vnode.v_tag, tagstr, sizeof(tagstr)); if (error == 0) { warnx("can't read v_tag at %p", (void *)vp); goto fail; } tagstr[sizeof(tagstr) - 1] = '\0'; /* * Find appropriate handler. */ for (i = 0, found = 0; i < NTYPES; i++) if (!strcmp(fstypes[i].tag, tagstr)) { if (fstypes[i].handler(kd, &vnode, vn) != 0) { goto fail; } break; } if (i == NTYPES) { if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "?(%s)", tagstr); return (1); } vn->vn_mntdir = getmnton(kd, vnode.v_mount); if ((vnode.v_type == VBLK || vnode.v_type == VCHR) && vnode.v_rdev != NULL){ vn->vn_dev = dev2udev(kd, vnode.v_rdev); (void)kdevtoname(kd, vnode.v_rdev, vn->vn_devname); } else { vn->vn_dev = -1; } return (0); fail: if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } /* * kinfo vnode type to filestat translation. */ static int kinfo_vtype2fst(int kfvtype) { static struct { int kf_vtype; int fst_vtype; } kfvtypes2fst[] = { { KF_VTYPE_VBAD, PS_FST_VTYPE_VBAD }, { KF_VTYPE_VBLK, PS_FST_VTYPE_VBLK }, { KF_VTYPE_VCHR, PS_FST_VTYPE_VCHR }, { KF_VTYPE_VDIR, PS_FST_VTYPE_VDIR }, { KF_VTYPE_VFIFO, PS_FST_VTYPE_VFIFO }, { KF_VTYPE_VLNK, PS_FST_VTYPE_VLNK }, { KF_VTYPE_VNON, PS_FST_VTYPE_VNON }, { KF_VTYPE_VREG, PS_FST_VTYPE_VREG }, { KF_VTYPE_VSOCK, PS_FST_VTYPE_VSOCK } }; #define NKFVTYPES (sizeof(kfvtypes2fst) / sizeof(*kfvtypes2fst)) unsigned int i; for (i = 0; i < NKFVTYPES; i++) if (kfvtypes2fst[i].kf_vtype == kfvtype) break; if (i == NKFVTYPES) return (PS_FST_VTYPE_UNKNOWN); return (kfvtypes2fst[i].fst_vtype); } static int procstat_get_vnode_info_sysctl(struct filestat *fst, struct vnstat *vn, char *errbuf) { struct statfs stbuf; struct kinfo_file *kif; struct kinfo_vmentry *kve; uint64_t fileid; uint64_t size; char *name, *path; uint32_t fsid; uint16_t mode; uint32_t rdev; int vntype; int status; assert(fst); assert(vn); bzero(vn, sizeof(*vn)); if (fst->fs_typedep == NULL) return (1); if (fst->fs_uflags & PS_FST_UFLAG_MMAP) { kve = fst->fs_typedep; fileid = kve->kve_vn_fileid; fsid = kve->kve_vn_fsid; mode = kve->kve_vn_mode; path = kve->kve_path; rdev = kve->kve_vn_rdev; size = kve->kve_vn_size; vntype = kinfo_vtype2fst(kve->kve_vn_type); status = kve->kve_status; } else { kif = fst->fs_typedep; fileid = kif->kf_un.kf_file.kf_file_fileid; fsid = kif->kf_un.kf_file.kf_file_fsid; mode = kif->kf_un.kf_file.kf_file_mode; path = kif->kf_path; rdev = kif->kf_un.kf_file.kf_file_rdev; size = kif->kf_un.kf_file.kf_file_size; vntype = kinfo_vtype2fst(kif->kf_vnode_type); status = kif->kf_status; } vn->vn_type = vntype; if (vntype == PS_FST_VTYPE_VNON || vntype == PS_FST_VTYPE_VBAD) return (0); if ((status & KF_ATTR_VALID) == 0) { if (errbuf != NULL) { snprintf(errbuf, _POSIX2_LINE_MAX, "? (no info available)"); } return (1); } if (path && *path) { statfs(path, &stbuf); vn->vn_mntdir = strdup(stbuf.f_mntonname); } else vn->vn_mntdir = strdup("-"); vn->vn_dev = rdev; if (vntype == PS_FST_VTYPE_VBLK) { name = devname(rdev, S_IFBLK); if (name != NULL) strlcpy(vn->vn_devname, name, sizeof(vn->vn_devname)); } else if (vntype == PS_FST_VTYPE_VCHR) { name = devname(vn->vn_dev, S_IFCHR); if (name != NULL) strlcpy(vn->vn_devname, name, sizeof(vn->vn_devname)); } vn->vn_fsid = fsid; vn->vn_fileid = fileid; vn->vn_size = size; vn->vn_mode = mode; return (0); } int procstat_get_socket_info(struct procstat *procstat, struct filestat *fst, struct sockstat *sock, char *errbuf) { assert(sock); if (procstat->type == PROCSTAT_KVM) { return (procstat_get_socket_info_kvm(procstat->kd, fst, sock, errbuf)); } else if (procstat->type == PROCSTAT_SYSCTL || procstat->type == PROCSTAT_CORE) { return (procstat_get_socket_info_sysctl(fst, sock, errbuf)); } else { warnx("unknown access method: %d", procstat->type); if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } } static int procstat_get_socket_info_kvm(kvm_t *kd, struct filestat *fst, struct sockstat *sock, char *errbuf) { struct domain dom; struct inpcb inpcb; struct protosw proto; struct socket s; struct unpcb unpcb; ssize_t len; void *so; assert(kd); assert(sock); assert(fst); bzero(sock, sizeof(*sock)); so = fst->fs_typedep; if (so == NULL) goto fail; sock->so_addr = (uintptr_t)so; /* fill in socket */ if (!kvm_read_all(kd, (unsigned long)so, &s, sizeof(struct socket))) { warnx("can't read sock at %p", (void *)so); goto fail; } /* fill in protosw entry */ if (!kvm_read_all(kd, (unsigned long)s.so_proto, &proto, sizeof(struct protosw))) { warnx("can't read protosw at %p", (void *)s.so_proto); goto fail; } /* fill in domain */ if (!kvm_read_all(kd, (unsigned long)proto.pr_domain, &dom, sizeof(struct domain))) { warnx("can't read domain at %p", (void *)proto.pr_domain); goto fail; } if ((len = kvm_read(kd, (unsigned long)dom.dom_name, sock->dname, sizeof(sock->dname) - 1)) < 0) { warnx("can't read domain name at %p", (void *)dom.dom_name); sock->dname[0] = '\0'; } else sock->dname[len] = '\0'; /* * Fill in known data. */ sock->type = s.so_type; sock->proto = proto.pr_protocol; sock->dom_family = dom.dom_family; sock->so_pcb = (uintptr_t)s.so_pcb; /* * Protocol specific data. */ switch(dom.dom_family) { case AF_INET: case AF_INET6: if (proto.pr_protocol == IPPROTO_TCP) { if (s.so_pcb) { if (kvm_read(kd, (u_long)s.so_pcb, (char *)&inpcb, sizeof(struct inpcb)) != sizeof(struct inpcb)) { warnx("can't read inpcb at %p", (void *)s.so_pcb); } else sock->inp_ppcb = (uintptr_t)inpcb.inp_ppcb; } } break; case AF_UNIX: if (s.so_pcb) { if (kvm_read(kd, (u_long)s.so_pcb, (char *)&unpcb, sizeof(struct unpcb)) != sizeof(struct unpcb)){ warnx("can't read unpcb at %p", (void *)s.so_pcb); } else if (unpcb.unp_conn) { sock->so_rcv_sb_state = s.so_rcv.sb_state; sock->so_snd_sb_state = s.so_snd.sb_state; sock->unp_conn = (uintptr_t)unpcb.unp_conn; } } break; default: break; } return (0); fail: if (errbuf != NULL) snprintf(errbuf, _POSIX2_LINE_MAX, "error"); return (1); } static int procstat_get_socket_info_sysctl(struct filestat *fst, struct sockstat *sock, char *errbuf __unused) { struct kinfo_file *kif; assert(sock); assert(fst); bzero(sock, sizeof(*sock)); kif = fst->fs_typedep; if (kif == NULL) return (0); /* * Fill in known data. */ sock->type = kif->kf_sock_type; sock->proto = kif->kf_sock_protocol; sock->dom_family = kif->kf_sock_domain; sock->so_pcb = kif->kf_un.kf_sock.kf_sock_pcb; strlcpy(sock->dname, kif->kf_path, sizeof(sock->dname)); bcopy(&kif->kf_sa_local, &sock->sa_local, kif->kf_sa_local.ss_len); bcopy(&kif->kf_sa_peer, &sock->sa_peer, kif->kf_sa_peer.ss_len); /* * Protocol specific data. */ switch(sock->dom_family) { case AF_INET: case AF_INET6: if (sock->proto == IPPROTO_TCP) sock->inp_ppcb = kif->kf_un.kf_sock.kf_sock_inpcb; break; case AF_UNIX: if (kif->kf_un.kf_sock.kf_sock_unpconn != 0) { sock->so_rcv_sb_state = kif->kf_un.kf_sock.kf_sock_rcv_sb_state; sock->so_snd_sb_state = kif->kf_un.kf_sock.kf_sock_snd_sb_state; sock->unp_conn = kif->kf_un.kf_sock.kf_sock_unpconn; } break; default: break; } return (0); } /* * Descriptor flags to filestat translation. */ static int to_filestat_flags(int flags) { static struct { int flag; int fst_flag; } fstflags[] = { { FREAD, PS_FST_FFLAG_READ }, { FWRITE, PS_FST_FFLAG_WRITE }, { O_APPEND, PS_FST_FFLAG_APPEND }, { O_ASYNC, PS_FST_FFLAG_ASYNC }, { O_CREAT, PS_FST_FFLAG_CREAT }, { O_DIRECT, PS_FST_FFLAG_DIRECT }, { O_EXCL, PS_FST_FFLAG_EXCL }, { O_EXEC, PS_FST_FFLAG_EXEC }, { O_EXLOCK, PS_FST_FFLAG_EXLOCK }, { O_NOFOLLOW, PS_FST_FFLAG_NOFOLLOW }, { O_NONBLOCK, PS_FST_FFLAG_NONBLOCK }, { O_SHLOCK, PS_FST_FFLAG_SHLOCK }, { O_SYNC, PS_FST_FFLAG_SYNC }, { O_TRUNC, PS_FST_FFLAG_TRUNC } }; #define NFSTFLAGS (sizeof(fstflags) / sizeof(*fstflags)) int fst_flags; unsigned int i; fst_flags = 0; for (i = 0; i < NFSTFLAGS; i++) if (flags & fstflags[i].flag) fst_flags |= fstflags[i].fst_flag; return (fst_flags); } /* * Vnode type to filestate translation. */ static int vntype2psfsttype(int type) { static struct { int vtype; int fst_vtype; } vt2fst[] = { { VBAD, PS_FST_VTYPE_VBAD }, { VBLK, PS_FST_VTYPE_VBLK }, { VCHR, PS_FST_VTYPE_VCHR }, { VDIR, PS_FST_VTYPE_VDIR }, { VFIFO, PS_FST_VTYPE_VFIFO }, { VLNK, PS_FST_VTYPE_VLNK }, { VNON, PS_FST_VTYPE_VNON }, { VREG, PS_FST_VTYPE_VREG }, { VSOCK, PS_FST_VTYPE_VSOCK } }; #define NVFTYPES (sizeof(vt2fst) / sizeof(*vt2fst)) unsigned int i, fst_type; fst_type = PS_FST_VTYPE_UNKNOWN; for (i = 0; i < NVFTYPES; i++) { if (type == vt2fst[i].vtype) { fst_type = vt2fst[i].fst_vtype; break; } } return (fst_type); } static char * getmnton(kvm_t *kd, struct mount *m) { struct mount mnt; static struct mtab { struct mtab *next; struct mount *m; char mntonname[MNAMELEN + 1]; } *mhead = NULL; struct mtab *mt; for (mt = mhead; mt != NULL; mt = mt->next) if (m == mt->m) return (mt->mntonname); if (!kvm_read_all(kd, (unsigned long)m, &mnt, sizeof(struct mount))) { warnx("can't read mount table at %p", (void *)m); return (NULL); } if ((mt = malloc(sizeof (struct mtab))) == NULL) err(1, NULL); mt->m = m; bcopy(&mnt.mnt_stat.f_mntonname[0], &mt->mntonname[0], MNAMELEN); mt->mntonname[MNAMELEN] = '\0'; mt->next = mhead; mhead = mt; return (mt->mntonname); } /* * Auxiliary structures and functions to get process environment or * command line arguments. */ struct argvec { char *buf; size_t bufsize; char **argv; size_t argc; }; static struct argvec * argvec_alloc(size_t bufsize) { struct argvec *av; av = malloc(sizeof(*av)); if (av == NULL) return (NULL); av->bufsize = bufsize; av->buf = malloc(av->bufsize); if (av->buf == NULL) { free(av); return (NULL); } av->argc = 32; av->argv = malloc(sizeof(char *) * av->argc); if (av->argv == NULL) { free(av->buf); free(av); return (NULL); } return av; } static void argvec_free(struct argvec * av) { free(av->argv); free(av->buf); free(av); } static char ** getargv(struct procstat *procstat, struct kinfo_proc *kp, size_t nchr, int env) { int error, name[4], argc, i; struct argvec *av, **avp; enum psc_type type; size_t len; char *p, **argv; assert(procstat); assert(kp); if (procstat->type == PROCSTAT_KVM) { warnx("can't use kvm access method"); return (NULL); } if (procstat->type != PROCSTAT_SYSCTL && procstat->type != PROCSTAT_CORE) { warnx("unknown access method: %d", procstat->type); return (NULL); } if (nchr == 0 || nchr > ARG_MAX) nchr = ARG_MAX; avp = (struct argvec **)(env ? &procstat->argv : &procstat->envv); av = *avp; if (av == NULL) { av = argvec_alloc(nchr); if (av == NULL) { warn("malloc(%zu)", nchr); return (NULL); } *avp = av; } else if (av->bufsize < nchr) { av->buf = reallocf(av->buf, nchr); if (av->buf == NULL) { warn("malloc(%zu)", nchr); return (NULL); } } if (procstat->type == PROCSTAT_SYSCTL) { name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = env ? KERN_PROC_ENV : KERN_PROC_ARGS; name[3] = kp->ki_pid; len = nchr; error = sysctl(name, 4, av->buf, &len, NULL, 0); if (error != 0 && errno != ESRCH && errno != EPERM) warn("sysctl(kern.proc.%s)", env ? "env" : "args"); if (error != 0 || len == 0) return (NULL); } else /* procstat->type == PROCSTAT_CORE */ { type = env ? PSC_TYPE_ENVV : PSC_TYPE_ARGV; len = nchr; if (procstat_core_get(procstat->core, type, av->buf, &len) == NULL) { return (NULL); } } argv = av->argv; argc = av->argc; i = 0; for (p = av->buf; p < av->buf + len; p += strlen(p) + 1) { argv[i++] = p; if (i < argc) continue; /* Grow argv. */ argc += argc; argv = realloc(argv, sizeof(char *) * argc); if (argv == NULL) { warn("malloc(%zu)", sizeof(char *) * argc); return (NULL); } av->argv = argv; av->argc = argc; } argv[i] = NULL; return (argv); } /* * Return process command line arguments. */ char ** procstat_getargv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr) { return (getargv(procstat, p, nchr, 0)); } /* * Free the buffer allocated by procstat_getargv(). */ void procstat_freeargv(struct procstat *procstat) { if (procstat->argv != NULL) { argvec_free(procstat->argv); procstat->argv = NULL; } } /* * Return process environment. */ char ** procstat_getenvv(struct procstat *procstat, struct kinfo_proc *p, size_t nchr) { return (getargv(procstat, p, nchr, 1)); } /* * Free the buffer allocated by procstat_getenvv(). */ void procstat_freeenvv(struct procstat *procstat) { if (procstat->envv != NULL) { argvec_free(procstat->envv); procstat->envv = NULL; } } static struct kinfo_vmentry * kinfo_getvmmap_core(struct procstat_core *core, int *cntp) { int cnt; size_t len; char *buf, *bp, *eb; struct kinfo_vmentry *kiv, *kp, *kv; buf = procstat_core_get(core, PSC_TYPE_VMMAP, NULL, &len); if (buf == NULL) return (NULL); /* * XXXMG: The code below is just copy&past from libutil. * The code duplication can be avoided if libutil * is extended to provide something like: * struct kinfo_vmentry *kinfo_getvmmap_from_buf(const char *buf, * size_t len, int *cntp); */ /* Pass 1: count items */ cnt = 0; bp = buf; eb = buf + len; while (bp < eb) { kv = (struct kinfo_vmentry *)(uintptr_t)bp; bp += kv->kve_structsize; cnt++; } kiv = calloc(cnt, sizeof(*kiv)); if (kiv == NULL) { free(buf); return (NULL); } bp = buf; eb = buf + len; kp = kiv; /* Pass 2: unpack */ while (bp < eb) { kv = (struct kinfo_vmentry *)(uintptr_t)bp; /* Copy/expand into pre-zeroed buffer */ memcpy(kp, kv, kv->kve_structsize); /* Advance to next packed record */ bp += kv->kve_structsize; /* Set field size to fixed length, advance */ kp->kve_structsize = sizeof(*kp); kp++; } free(buf); *cntp = cnt; return (kiv); /* Caller must free() return value */ } struct kinfo_vmentry * procstat_getvmmap(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *cntp) { switch(procstat->type) { case PROCSTAT_KVM: warnx("kvm method is not supported"); return (NULL); case PROCSTAT_SYSCTL: return (kinfo_getvmmap(kp->ki_pid, cntp)); case PROCSTAT_CORE: return (kinfo_getvmmap_core(procstat->core, cntp)); default: warnx("unknown access method: %d", procstat->type); return (NULL); } } void procstat_freevmmap(struct procstat *procstat __unused, struct kinfo_vmentry *vmmap) { free(vmmap); } static gid_t * procstat_getgroups_kvm(kvm_t *kd, struct kinfo_proc *kp, unsigned int *cntp) { struct proc proc; struct ucred ucred; gid_t *groups; size_t len; assert(kd != NULL); assert(kp != NULL); if (!kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc, sizeof(proc))) { warnx("can't read proc struct at %p for pid %d", kp->ki_paddr, kp->ki_pid); return (NULL); } if (proc.p_ucred == NOCRED) return (NULL); if (!kvm_read_all(kd, (unsigned long)proc.p_ucred, &ucred, sizeof(ucred))) { warnx("can't read ucred struct at %p for pid %d", proc.p_ucred, kp->ki_pid); return (NULL); } len = ucred.cr_ngroups * sizeof(gid_t); groups = malloc(len); if (groups == NULL) { warn("malloc(%zu)", len); return (NULL); } if (!kvm_read_all(kd, (unsigned long)ucred.cr_groups, groups, len)) { warnx("can't read groups at %p for pid %d", ucred.cr_groups, kp->ki_pid); free(groups); return (NULL); } *cntp = ucred.cr_ngroups; return (groups); } static gid_t * procstat_getgroups_sysctl(pid_t pid, unsigned int *cntp) { int mib[4]; size_t len; gid_t *groups; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_GROUPS; mib[3] = pid; len = (sysconf(_SC_NGROUPS_MAX) + 1) * sizeof(gid_t); groups = malloc(len); if (groups == NULL) { warn("malloc(%zu)", len); return (NULL); } if (sysctl(mib, 4, groups, &len, NULL, 0) == -1) { warn("sysctl: kern.proc.groups: %d", pid); free(groups); return (NULL); } *cntp = len / sizeof(gid_t); return (groups); } static gid_t * procstat_getgroups_core(struct procstat_core *core, unsigned int *cntp) { size_t len; gid_t *groups; groups = procstat_core_get(core, PSC_TYPE_GROUPS, NULL, &len); if (groups == NULL) return (NULL); *cntp = len / sizeof(gid_t); return (groups); } gid_t * procstat_getgroups(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *cntp) { switch(procstat->type) { case PROCSTAT_KVM: return (procstat_getgroups_kvm(procstat->kd, kp, cntp)); case PROCSTAT_SYSCTL: return (procstat_getgroups_sysctl(kp->ki_pid, cntp)); case PROCSTAT_CORE: return (procstat_getgroups_core(procstat->core, cntp)); default: warnx("unknown access method: %d", procstat->type); return (NULL); } } void procstat_freegroups(struct procstat *procstat __unused, gid_t *groups) { free(groups); } static int procstat_getumask_kvm(kvm_t *kd, struct kinfo_proc *kp, unsigned short *maskp) { struct filedesc fd; assert(kd != NULL); assert(kp != NULL); if (kp->ki_fd == NULL) return (-1); if (!kvm_read_all(kd, (unsigned long)kp->ki_fd, &fd, sizeof(fd))) { warnx("can't read filedesc at %p for pid %d", kp->ki_fd, kp->ki_pid); return (-1); } *maskp = fd.fd_cmask; return (0); } static int procstat_getumask_sysctl(pid_t pid, unsigned short *maskp) { int error; int mib[4]; size_t len; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_UMASK; mib[3] = pid; len = sizeof(*maskp); error = sysctl(mib, 4, maskp, &len, NULL, 0); if (error != 0 && errno != ESRCH && errno != EPERM) warn("sysctl: kern.proc.umask: %d", pid); return (error); } static int procstat_getumask_core(struct procstat_core *core, unsigned short *maskp) { size_t len; unsigned short *buf; buf = procstat_core_get(core, PSC_TYPE_UMASK, NULL, &len); if (buf == NULL) return (-1); if (len < sizeof(*maskp)) { free(buf); return (-1); } *maskp = *buf; free(buf); return (0); } int procstat_getumask(struct procstat *procstat, struct kinfo_proc *kp, unsigned short *maskp) { switch(procstat->type) { case PROCSTAT_KVM: return (procstat_getumask_kvm(procstat->kd, kp, maskp)); case PROCSTAT_SYSCTL: return (procstat_getumask_sysctl(kp->ki_pid, maskp)); case PROCSTAT_CORE: return (procstat_getumask_core(procstat->core, maskp)); default: warnx("unknown access method: %d", procstat->type); return (-1); } } static int procstat_getrlimit_kvm(kvm_t *kd, struct kinfo_proc *kp, int which, struct rlimit* rlimit) { struct proc proc; unsigned long offset; assert(kd != NULL); assert(kp != NULL); assert(which >= 0 && which < RLIM_NLIMITS); if (!kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc, sizeof(proc))) { warnx("can't read proc struct at %p for pid %d", kp->ki_paddr, kp->ki_pid); return (-1); } if (proc.p_limit == NULL) return (-1); offset = (unsigned long)proc.p_limit + sizeof(struct rlimit) * which; if (!kvm_read_all(kd, offset, rlimit, sizeof(*rlimit))) { warnx("can't read rlimit struct at %p for pid %d", (void *)offset, kp->ki_pid); return (-1); } return (0); } static int procstat_getrlimit_sysctl(pid_t pid, int which, struct rlimit* rlimit) { int error, name[5]; size_t len; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_RLIMIT; name[3] = pid; name[4] = which; len = sizeof(struct rlimit); error = sysctl(name, 5, rlimit, &len, NULL, 0); if (error < 0 && errno != ESRCH) { warn("sysctl: kern.proc.rlimit: %d", pid); return (-1); } if (error < 0 || len != sizeof(struct rlimit)) return (-1); return (0); } static int procstat_getrlimit_core(struct procstat_core *core, int which, struct rlimit* rlimit) { size_t len; struct rlimit* rlimits; if (which < 0 || which >= RLIM_NLIMITS) { errno = EINVAL; warn("getrlimit: which"); return (-1); } rlimits = procstat_core_get(core, PSC_TYPE_RLIMIT, NULL, &len); if (rlimits == NULL) return (-1); if (len < sizeof(struct rlimit) * RLIM_NLIMITS) { free(rlimits); return (-1); } *rlimit = rlimits[which]; return (0); } int procstat_getrlimit(struct procstat *procstat, struct kinfo_proc *kp, int which, struct rlimit* rlimit) { switch(procstat->type) { case PROCSTAT_KVM: return (procstat_getrlimit_kvm(procstat->kd, kp, which, rlimit)); case PROCSTAT_SYSCTL: return (procstat_getrlimit_sysctl(kp->ki_pid, which, rlimit)); case PROCSTAT_CORE: return (procstat_getrlimit_core(procstat->core, which, rlimit)); default: warnx("unknown access method: %d", procstat->type); return (-1); } } static int procstat_getpathname_sysctl(pid_t pid, char *pathname, size_t maxlen) { int error, name[4]; size_t len; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_PATHNAME; name[3] = pid; len = maxlen; error = sysctl(name, 4, pathname, &len, NULL, 0); if (error != 0 && errno != ESRCH) warn("sysctl: kern.proc.pathname: %d", pid); if (len == 0) pathname[0] = '\0'; return (error); } static int procstat_getpathname_core(struct procstat_core *core, char *pathname, size_t maxlen) { struct kinfo_file *files; int cnt, i, result; files = kinfo_getfile_core(core, &cnt); if (files == NULL) return (-1); result = -1; for (i = 0; i < cnt; i++) { if (files[i].kf_fd != KF_FD_TYPE_TEXT) continue; strncpy(pathname, files[i].kf_path, maxlen); result = 0; break; } free(files); return (result); } int procstat_getpathname(struct procstat *procstat, struct kinfo_proc *kp, char *pathname, size_t maxlen) { switch(procstat->type) { case PROCSTAT_KVM: /* XXX: Return empty string. */ if (maxlen > 0) pathname[0] = '\0'; return (0); case PROCSTAT_SYSCTL: return (procstat_getpathname_sysctl(kp->ki_pid, pathname, maxlen)); case PROCSTAT_CORE: return (procstat_getpathname_core(procstat->core, pathname, maxlen)); default: warnx("unknown access method: %d", procstat->type); return (-1); } } static int procstat_getosrel_kvm(kvm_t *kd, struct kinfo_proc *kp, int *osrelp) { struct proc proc; assert(kd != NULL); assert(kp != NULL); if (!kvm_read_all(kd, (unsigned long)kp->ki_paddr, &proc, sizeof(proc))) { warnx("can't read proc struct at %p for pid %d", kp->ki_paddr, kp->ki_pid); return (-1); } *osrelp = proc.p_osrel; return (0); } static int procstat_getosrel_sysctl(pid_t pid, int *osrelp) { int error, name[4]; size_t len; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_OSREL; name[3] = pid; len = sizeof(*osrelp); error = sysctl(name, 4, osrelp, &len, NULL, 0); if (error != 0 && errno != ESRCH) warn("sysctl: kern.proc.osrel: %d", pid); return (error); } static int procstat_getosrel_core(struct procstat_core *core, int *osrelp) { size_t len; int *buf; buf = procstat_core_get(core, PSC_TYPE_OSREL, NULL, &len); if (buf == NULL) return (-1); if (len < sizeof(*osrelp)) { free(buf); return (-1); } *osrelp = *buf; free(buf); return (0); } int procstat_getosrel(struct procstat *procstat, struct kinfo_proc *kp, int *osrelp) { switch(procstat->type) { case PROCSTAT_KVM: return (procstat_getosrel_kvm(procstat->kd, kp, osrelp)); case PROCSTAT_SYSCTL: return (procstat_getosrel_sysctl(kp->ki_pid, osrelp)); case PROCSTAT_CORE: return (procstat_getosrel_core(procstat->core, osrelp)); default: warnx("unknown access method: %d", procstat->type); return (-1); } } #define PROC_AUXV_MAX 256 #if __ELF_WORD_SIZE == 64 static const char *elf32_sv_names[] = { "Linux ELF32", "FreeBSD ELF32", }; static int is_elf32_sysctl(pid_t pid) { int error, name[4]; size_t len, i; static char sv_name[256]; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_SV_NAME; name[3] = pid; len = sizeof(sv_name); error = sysctl(name, 4, sv_name, &len, NULL, 0); if (error != 0 || len == 0) return (0); for (i = 0; i < sizeof(elf32_sv_names) / sizeof(*elf32_sv_names); i++) { if (strncmp(sv_name, elf32_sv_names[i], sizeof(sv_name)) == 0) return (1); } return (0); } static Elf_Auxinfo * procstat_getauxv32_sysctl(pid_t pid, unsigned int *cntp) { Elf_Auxinfo *auxv; Elf32_Auxinfo *auxv32; void *ptr; size_t len; unsigned int i, count; int name[4]; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_AUXV; name[3] = pid; len = PROC_AUXV_MAX * sizeof(Elf32_Auxinfo); auxv = NULL; auxv32 = malloc(len); if (auxv32 == NULL) { warn("malloc(%zu)", len); goto out; } if (sysctl(name, 4, auxv32, &len, NULL, 0) == -1) { if (errno != ESRCH && errno != EPERM) warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); goto out; } count = len / sizeof(Elf_Auxinfo); auxv = malloc(count * sizeof(Elf_Auxinfo)); if (auxv == NULL) { warn("malloc(%zu)", count * sizeof(Elf_Auxinfo)); goto out; } for (i = 0; i < count; i++) { /* * XXX: We expect that values for a_type on a 32-bit platform * are directly mapped to values on 64-bit one, which is not * necessarily true. */ auxv[i].a_type = auxv32[i].a_type; ptr = &auxv32[i].a_un; auxv[i].a_un.a_val = *((uint32_t *)ptr); } *cntp = count; out: free(auxv32); return (auxv); } #endif /* __ELF_WORD_SIZE == 64 */ static Elf_Auxinfo * procstat_getauxv_sysctl(pid_t pid, unsigned int *cntp) { Elf_Auxinfo *auxv; int name[4]; size_t len; #if __ELF_WORD_SIZE == 64 if (is_elf32_sysctl(pid)) return (procstat_getauxv32_sysctl(pid, cntp)); #endif name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_AUXV; name[3] = pid; len = PROC_AUXV_MAX * sizeof(Elf_Auxinfo); auxv = malloc(len); if (auxv == NULL) { warn("malloc(%zu)", len); return (NULL); } if (sysctl(name, 4, auxv, &len, NULL, 0) == -1) { if (errno != ESRCH && errno != EPERM) warn("sysctl: kern.proc.auxv: %d: %d", pid, errno); free(auxv); return (NULL); } *cntp = len / sizeof(Elf_Auxinfo); return (auxv); } static Elf_Auxinfo * procstat_getauxv_core(struct procstat_core *core, unsigned int *cntp) { Elf_Auxinfo *auxv; size_t len; auxv = procstat_core_get(core, PSC_TYPE_AUXV, NULL, &len); if (auxv == NULL) return (NULL); *cntp = len / sizeof(Elf_Auxinfo); return (auxv); } Elf_Auxinfo * procstat_getauxv(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *cntp) { switch(procstat->type) { case PROCSTAT_KVM: warnx("kvm method is not supported"); return (NULL); case PROCSTAT_SYSCTL: return (procstat_getauxv_sysctl(kp->ki_pid, cntp)); case PROCSTAT_CORE: return (procstat_getauxv_core(procstat->core, cntp)); default: warnx("unknown access method: %d", procstat->type); return (NULL); } } void procstat_freeauxv(struct procstat *procstat __unused, Elf_Auxinfo *auxv) { free(auxv); } static struct kinfo_kstack * procstat_getkstack_sysctl(pid_t pid, int *cntp) { struct kinfo_kstack *kkstp; int error, name[4]; size_t len; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_KSTACK; name[3] = pid; len = 0; error = sysctl(name, 4, NULL, &len, NULL, 0); if (error < 0 && errno != ESRCH && errno != EPERM && errno != ENOENT) { warn("sysctl: kern.proc.kstack: %d", pid); return (NULL); } if (error == -1 && errno == ENOENT) { warnx("sysctl: kern.proc.kstack unavailable" " (options DDB or options STACK required in kernel)"); return (NULL); } if (error == -1) return (NULL); kkstp = malloc(len); if (kkstp == NULL) { warn("malloc(%zu)", len); return (NULL); } if (sysctl(name, 4, kkstp, &len, NULL, 0) == -1) { warn("sysctl: kern.proc.pid: %d", pid); free(kkstp); return (NULL); } *cntp = len / sizeof(*kkstp); return (kkstp); } struct kinfo_kstack * procstat_getkstack(struct procstat *procstat, struct kinfo_proc *kp, unsigned int *cntp) { switch(procstat->type) { case PROCSTAT_KVM: warnx("kvm method is not supported"); return (NULL); case PROCSTAT_SYSCTL: return (procstat_getkstack_sysctl(kp->ki_pid, cntp)); case PROCSTAT_CORE: warnx("core method is not supported"); return (NULL); default: warnx("unknown access method: %d", procstat->type); return (NULL); } } void procstat_freekstack(struct procstat *procstat __unused, struct kinfo_kstack *kkstp) { free(kkstp); } Index: stable/10/sbin/dhclient/bpf.c =================================================================== --- stable/10/sbin/dhclient/bpf.c (revision 280249) +++ stable/10/sbin/dhclient/bpf.c (revision 280250) @@ -1,487 +1,487 @@ /* $OpenBSD: bpf.c,v 1.13 2004/05/05 14:28:58 deraadt Exp $ */ /* BPF socket interface code, originally contributed by Archie Cobbs. */ /* * Copyright (c) 1995, 1996, 1998, 1999 * The Internet Software Consortium. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This software has been written for the Internet Software Consortium * by Ted Lemon in cooperation with Vixie * Enterprises. To learn more about the Internet Software Consortium, * see ``http://www.vix.com/isc''. To learn more about Vixie * Enterprises, see ``http://www.vix.com''. */ #include __FBSDID("$FreeBSD$"); -#include +#include #include "dhcpd.h" #include "privsep.h" -#include +#include #include #include #include #include #include #include #include #define BPF_FORMAT "/dev/bpf%d" /* * Called by get_interface_list for each interface that's discovered. * Opens a packet filter for each interface and adds it to the select * mask. */ int if_register_bpf(struct interface_info *info, int flags) { char filename[50]; int sock, b; /* Open a BPF device */ for (b = 0;; b++) { snprintf(filename, sizeof(filename), BPF_FORMAT, b); sock = open(filename, flags); if (sock < 0) { if (errno == EBUSY) continue; else error("Can't find free bpf: %m"); } else break; } /* Set the BPF device to point at this interface. */ if (ioctl(sock, BIOCSETIF, info->ifp) < 0) error("Can't attach interface %s to bpf device %s: %m", info->name, filename); return (sock); } /* * Packet write filter program: * 'ip and udp and src port bootps and dst port (bootps or bootpc)' */ struct bpf_insn dhcp_bpf_wfilter[] = { BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12), /* Make sure this is an IP packet... */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10), /* Make sure it's a UDP packet... */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8), /* Make sure this isn't a fragment... */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */ /* Get the IP header length... */ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), /* Make sure it's from the right port... */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3), /* Make sure it is to the right ports ... */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* If we passed all the tests, ask for the whole packet. */ BPF_STMT(BPF_RET+BPF_K, (u_int)-1), /* Otherwise, drop it. */ BPF_STMT(BPF_RET+BPF_K, 0), }; int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn); void if_register_send(struct interface_info *info) { cap_rights_t rights; struct bpf_version v; struct bpf_program p; int sock, on = 1; /* Open a BPF device and hang it on this interface... */ info->wfdesc = if_register_bpf(info, O_WRONLY); /* Make sure the BPF version is in range... */ if (ioctl(info->wfdesc, BIOCVERSION, &v) < 0) error("Can't get BPF version: %m"); if (v.bv_major != BPF_MAJOR_VERSION || v.bv_minor < BPF_MINOR_VERSION) error("Kernel BPF version out of range - recompile dhcpd!"); /* Set up the bpf write filter program structure. */ p.bf_len = dhcp_bpf_wfilter_len; p.bf_insns = dhcp_bpf_wfilter; if (dhcp_bpf_wfilter[7].k == 0x1fff) dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK); if (ioctl(info->wfdesc, BIOCSETWF, &p) < 0) error("Can't install write filter program: %m"); if (ioctl(info->wfdesc, BIOCLOCK, NULL) < 0) error("Cannot lock bpf"); cap_rights_init(&rights, CAP_WRITE); if (cap_rights_limit(info->wfdesc, &rights) < 0 && errno != ENOSYS) error("Can't limit bpf descriptor: %m"); /* * Use raw socket for unicast send. */ if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) == -1) error("socket(SOCK_RAW): %m"); if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1) error("setsockopt(IP_HDRINCL): %m"); info->ufdesc = sock; } /* * Packet filter program... * * XXX: Changes to the filter program may require changes to the * constant offsets used in if_register_send to patch the BPF program! */ struct bpf_insn dhcp_bpf_filter[] = { /* Make sure this is an IP packet... */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), /* Make sure it's a UDP packet... */ BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), /* Make sure this isn't a fragment... */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), /* Get the IP header length... */ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), /* Make sure it's to the right port... */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 67, 0, 1), /* patch */ /* If we passed all the tests, ask for the whole packet. */ BPF_STMT(BPF_RET+BPF_K, (u_int)-1), /* Otherwise, drop it. */ BPF_STMT(BPF_RET+BPF_K, 0), }; int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn); void if_register_receive(struct interface_info *info) { static const unsigned long cmds[2] = { SIOCGIFFLAGS, SIOCGIFMEDIA }; cap_rights_t rights; struct bpf_version v; struct bpf_program p; int flag = 1, sz; /* Open a BPF device and hang it on this interface... */ info->rfdesc = if_register_bpf(info, O_RDONLY); /* Make sure the BPF version is in range... */ if (ioctl(info->rfdesc, BIOCVERSION, &v) < 0) error("Can't get BPF version: %m"); if (v.bv_major != BPF_MAJOR_VERSION || v.bv_minor < BPF_MINOR_VERSION) error("Kernel BPF version out of range - recompile dhcpd!"); /* * Set immediate mode so that reads return as soon as a packet * comes in, rather than waiting for the input buffer to fill * with packets. */ if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) < 0) error("Can't set immediate mode on bpf device: %m"); /* Get the required BPF buffer length from the kernel. */ if (ioctl(info->rfdesc, BIOCGBLEN, &sz) < 0) error("Can't get bpf buffer length: %m"); info->rbuf_max = sz; info->rbuf = malloc(info->rbuf_max); if (!info->rbuf) error("Can't allocate %lu bytes for bpf input buffer.", (unsigned long)info->rbuf_max); info->rbuf_offset = 0; info->rbuf_len = 0; /* Set up the bpf filter program structure. */ p.bf_len = dhcp_bpf_filter_len; p.bf_insns = dhcp_bpf_filter; /* Patch the server port into the BPF program... * * XXX: changes to filter program may require changes to the * insn number(s) used below! */ dhcp_bpf_filter[8].k = LOCAL_PORT; if (ioctl(info->rfdesc, BIOCSETF, &p) < 0) error("Can't install packet filter program: %m"); if (ioctl(info->rfdesc, BIOCLOCK, NULL) < 0) error("Cannot lock bpf"); cap_rights_init(&rights, CAP_IOCTL, CAP_EVENT, CAP_READ); if (cap_rights_limit(info->rfdesc, &rights) < 0 && errno != ENOSYS) error("Can't limit bpf descriptor: %m"); if (cap_ioctls_limit(info->rfdesc, cmds, 2) < 0 && errno != ENOSYS) error("Can't limit ioctls for bpf descriptor: %m"); } void send_packet_unpriv(int privfd, struct dhcp_packet *raw, size_t len, struct in_addr from, struct in_addr to) { struct imsg_hdr hdr; struct buf *buf; int errs; hdr.code = IMSG_SEND_PACKET; hdr.len = sizeof(hdr) + sizeof(size_t) + len + sizeof(from) + sizeof(to); if ((buf = buf_open(hdr.len)) == NULL) error("buf_open: %m"); errs = 0; errs += buf_add(buf, &hdr, sizeof(hdr)); errs += buf_add(buf, &len, sizeof(len)); errs += buf_add(buf, raw, len); errs += buf_add(buf, &from, sizeof(from)); errs += buf_add(buf, &to, sizeof(to)); if (errs) error("buf_add: %m"); if (buf_close(privfd, buf) == -1) error("buf_close: %m"); } void send_packet_priv(struct interface_info *interface, struct imsg_hdr *hdr, int fd) { unsigned char buf[256]; struct iovec iov[2]; struct msghdr msg; struct dhcp_packet raw; size_t len; struct in_addr from, to; int result, bufp = 0; if (hdr->len < sizeof(*hdr) + sizeof(size_t)) error("corrupted message received"); buf_read(fd, &len, sizeof(len)); if (hdr->len != sizeof(*hdr) + sizeof(size_t) + len + sizeof(from) + sizeof(to)) { error("corrupted message received"); } if (len > sizeof(raw)) error("corrupted message received"); buf_read(fd, &raw, len); buf_read(fd, &from, sizeof(from)); buf_read(fd, &to, sizeof(to)); /* Assemble the headers... */ if (to.s_addr == INADDR_BROADCAST) assemble_hw_header(interface, buf, &bufp); assemble_udp_ip_header(buf, &bufp, from.s_addr, to.s_addr, htons(REMOTE_PORT), (unsigned char *)&raw, len); iov[0].iov_base = buf; iov[0].iov_len = bufp; iov[1].iov_base = &raw; iov[1].iov_len = len; /* Fire it off */ if (to.s_addr == INADDR_BROADCAST) result = writev(interface->wfdesc, iov, 2); else { struct sockaddr_in sato; sato.sin_addr = to; sato.sin_port = htons(REMOTE_PORT); sato.sin_family = AF_INET; sato.sin_len = sizeof(sato); memset(&msg, 0, sizeof(msg)); msg.msg_name = (struct sockaddr *)&sato; msg.msg_namelen = sizeof(sato); msg.msg_iov = iov; msg.msg_iovlen = 2; result = sendmsg(interface->ufdesc, &msg, 0); } if (result < 0) warning("send_packet: %m"); } ssize_t receive_packet(struct interface_info *interface, unsigned char *buf, size_t len, struct sockaddr_in *from, struct hardware *hfrom) { int length = 0, offset = 0; struct bpf_hdr hdr; /* * All this complexity is because BPF doesn't guarantee that * only one packet will be returned at a time. We're getting * what we deserve, though - this is a terrible abuse of the BPF * interface. Sigh. */ /* Process packets until we get one we can return or until we've * done a read and gotten nothing we can return... */ do { /* If the buffer is empty, fill it. */ if (interface->rbuf_offset >= interface->rbuf_len) { length = read(interface->rfdesc, interface->rbuf, interface->rbuf_max); if (length <= 0) return (length); interface->rbuf_offset = 0; interface->rbuf_len = length; } /* * If there isn't room for a whole bpf header, something * went wrong, but we'll ignore it and hope it goes * away... XXX */ if (interface->rbuf_len - interface->rbuf_offset < sizeof(hdr)) { interface->rbuf_offset = interface->rbuf_len; continue; } /* Copy out a bpf header... */ memcpy(&hdr, &interface->rbuf[interface->rbuf_offset], sizeof(hdr)); /* * If the bpf header plus data doesn't fit in what's * left of the buffer, stick head in sand yet again... */ if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen > interface->rbuf_len) { interface->rbuf_offset = interface->rbuf_len; continue; } /* Skip over the BPF header... */ interface->rbuf_offset += hdr.bh_hdrlen; /* * If the captured data wasn't the whole packet, or if * the packet won't fit in the input buffer, all we can * do is drop it. */ if (hdr.bh_caplen != hdr.bh_datalen) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); continue; } /* Decode the physical header... */ offset = decode_hw_header(interface->rbuf, interface->rbuf_offset, hfrom); /* * If a physical layer checksum failed (dunno of any * physical layer that supports this, but WTH), skip * this packet. */ if (offset < 0) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); continue; } interface->rbuf_offset += offset; hdr.bh_caplen -= offset; /* Decode the IP and UDP headers... */ offset = decode_udp_ip_header(interface->rbuf, interface->rbuf_offset, from, NULL, hdr.bh_caplen); /* If the IP or UDP checksum was bad, skip the packet... */ if (offset < 0) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); continue; } interface->rbuf_offset += offset; hdr.bh_caplen -= offset; /* * If there's not enough room to stash the packet data, * we have to skip it (this shouldn't happen in real * life, though). */ if (hdr.bh_caplen > len) { interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); continue; } /* Copy out the data in the packet... */ memcpy(buf, interface->rbuf + interface->rbuf_offset, hdr.bh_caplen); interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + hdr.bh_caplen); return (hdr.bh_caplen); } while (!length); return (0); } Index: stable/10/sbin/dhclient/dhclient.c =================================================================== --- stable/10/sbin/dhclient/dhclient.c (revision 280249) +++ stable/10/sbin/dhclient/dhclient.c (revision 280250) @@ -1,2758 +1,2758 @@ /* $OpenBSD: dhclient.c,v 1.63 2005/02/06 17:10:13 krw Exp $ */ /* * Copyright 2004 Henning Brauer * Copyright (c) 1995, 1996, 1997, 1998, 1999 * The Internet Software Consortium. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The Internet Software Consortium nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * This software has been written for the Internet Software Consortium * by Ted Lemon in cooperation with Vixie * Enterprises. To learn more about the Internet Software Consortium, * see ``http://www.vix.com/isc''. To learn more about Vixie * Enterprises, see ``http://www.vix.com''. * * This client was substantially modified and enhanced by Elliot Poger * for use on Linux while he was working on the MosquitoNet project at * Stanford. * * The current version owes much to Elliot's Linux enhancements, but * was substantially reorganized and partially rewritten by Ted Lemon * so as to use the same networking framework that the Internet Software * Consortium DHCP server uses. Much system-specific configuration code * was moved into a shell script so that as support for more operating * systems is added, it will not be necessary to port and maintain * system-specific configuration code to these operating systems - instead, * the shell script can invoke the native tools to accomplish the same * purpose. */ #include __FBSDID("$FreeBSD$"); -#include +#include #include "dhcpd.h" #include "privsep.h" -#include +#include #include #ifndef _PATH_VAREMPTY #define _PATH_VAREMPTY "/var/empty" #endif #define PERIOD 0x2e #define hyphenchar(c) ((c) == 0x2d) #define bslashchar(c) ((c) == 0x5c) #define periodchar(c) ((c) == PERIOD) #define asterchar(c) ((c) == 0x2a) #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \ ((c) >= 0x61 && (c) <= 0x7a)) #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) #define whitechar(c) ((c) == ' ' || (c) == '\t') #define borderchar(c) (alphachar(c) || digitchar(c)) #define middlechar(c) (borderchar(c) || hyphenchar(c)) #define domainchar(c) ((c) > 0x20 && (c) < 0x7f) #define CLIENT_PATH "PATH=/usr/bin:/usr/sbin:/bin:/sbin" time_t cur_time; time_t default_lease_time = 43200; /* 12 hours... */ char *path_dhclient_conf = _PATH_DHCLIENT_CONF; char *path_dhclient_db = NULL; int log_perror = 1; int privfd; int nullfd = -1; char hostname[_POSIX_HOST_NAME_MAX + 1]; struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } }; struct in_addr inaddr_any, inaddr_broadcast; char *path_dhclient_pidfile; struct pidfh *pidfile; /* * ASSERT_STATE() does nothing now; it used to be * assert (state_is == state_shouldbe). */ #define ASSERT_STATE(state_is, state_shouldbe) {} #define TIME_MAX 2147483647 int log_priority; int no_daemon; int unknown_ok = 1; int routefd; struct interface_info *ifi; int findproto(char *, int); struct sockaddr *get_ifa(char *, int); void routehandler(struct protocol *); void usage(void); int check_option(struct client_lease *l, int option); int check_classless_option(unsigned char *data, int len); int ipv4addrs(char * buf); int res_hnok(const char *dn); int check_search(const char *srch); char *option_as_string(unsigned int code, unsigned char *data, int len); int fork_privchld(int, int); #define ROUNDUP(a) \ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) static time_t scripttime; int findproto(char *cp, int n) { struct sockaddr *sa; int i; if (n == 0) return -1; for (i = 1; i; i <<= 1) { if (i & n) { sa = (struct sockaddr *)cp; switch (i) { case RTA_IFA: case RTA_DST: case RTA_GATEWAY: case RTA_NETMASK: if (sa->sa_family == AF_INET) return AF_INET; if (sa->sa_family == AF_INET6) return AF_INET6; break; case RTA_IFP: break; } ADVANCE(cp, sa); } } return (-1); } struct sockaddr * get_ifa(char *cp, int n) { struct sockaddr *sa; int i; if (n == 0) return (NULL); for (i = 1; i; i <<= 1) if (i & n) { sa = (struct sockaddr *)cp; if (i == RTA_IFA) return (sa); ADVANCE(cp, sa); } return (NULL); } struct iaddr defaddr = { 4 }; uint8_t curbssid[6]; static void disassoc(void *arg) { struct interface_info *ifi = arg; /* * Clear existing state. */ if (ifi->client->active != NULL) { script_init("EXPIRE", NULL); script_write_params("old_", ifi->client->active); if (ifi->client->alias) script_write_params("alias_", ifi->client->alias); script_go(); } ifi->client->state = S_INIT; } /* ARGSUSED */ void routehandler(struct protocol *p) { char msg[2048], *addr; struct rt_msghdr *rtm; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct if_announcemsghdr *ifan; struct ieee80211_join_event *jev; struct client_lease *l; time_t t = time(NULL); struct sockaddr *sa; struct iaddr a; ssize_t n; int linkstat; n = read(routefd, &msg, sizeof(msg)); rtm = (struct rt_msghdr *)msg; if (n < sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen || rtm->rtm_version != RTM_VERSION) return; switch (rtm->rtm_type) { case RTM_NEWADDR: case RTM_DELADDR: ifam = (struct ifa_msghdr *)rtm; if (ifam->ifam_index != ifi->index) break; if (findproto((char *)(ifam + 1), ifam->ifam_addrs) != AF_INET) break; if (scripttime == 0 || t < scripttime + 10) break; sa = get_ifa((char *)(ifam + 1), ifam->ifam_addrs); if (sa == NULL) break; if ((a.len = sizeof(struct in_addr)) > sizeof(a.iabuf)) error("king bula sez: len mismatch"); memcpy(a.iabuf, &((struct sockaddr_in *)sa)->sin_addr, a.len); if (addr_eq(a, defaddr)) break; for (l = ifi->client->active; l != NULL; l = l->next) if (addr_eq(a, l->address)) break; if (l == NULL) /* added/deleted addr is not the one we set */ break; addr = inet_ntoa(((struct sockaddr_in *)sa)->sin_addr); if (rtm->rtm_type == RTM_NEWADDR) { /* * XXX: If someone other than us adds our address, * should we assume they are taking over from us, * delete the lease record, and exit without modifying * the interface? */ warning("My address (%s) was re-added", addr); } else { warning("My address (%s) was deleted, dhclient exiting", addr); goto die; } break; case RTM_IFINFO: ifm = (struct if_msghdr *)rtm; if (ifm->ifm_index != ifi->index) break; if ((rtm->rtm_flags & RTF_UP) == 0) { warning("Interface %s is down, dhclient exiting", ifi->name); goto die; } linkstat = interface_link_status(ifi->name); if (linkstat != ifi->linkstat) { debug("%s link state %s -> %s", ifi->name, ifi->linkstat ? "up" : "down", linkstat ? "up" : "down"); ifi->linkstat = linkstat; if (linkstat) state_reboot(ifi); } break; case RTM_IFANNOUNCE: ifan = (struct if_announcemsghdr *)rtm; if (ifan->ifan_what == IFAN_DEPARTURE && ifan->ifan_index == ifi->index) { warning("Interface %s is gone, dhclient exiting", ifi->name); goto die; } break; case RTM_IEEE80211: ifan = (struct if_announcemsghdr *)rtm; if (ifan->ifan_index != ifi->index) break; switch (ifan->ifan_what) { case RTM_IEEE80211_ASSOC: case RTM_IEEE80211_REASSOC: /* * Use assoc/reassoc event to kick state machine * in case we roam. Otherwise fall back to the * normal state machine just like a wired network. */ jev = (struct ieee80211_join_event *) &ifan[1]; if (memcmp(curbssid, jev->iev_addr, 6)) { disassoc(ifi); state_reboot(ifi); } memcpy(curbssid, jev->iev_addr, 6); break; } break; default: break; } return; die: script_init("FAIL", NULL); if (ifi->client->alias) script_write_params("alias_", ifi->client->alias); script_go(); if (pidfile != NULL) pidfile_remove(pidfile); exit(1); } int main(int argc, char *argv[]) { extern char *__progname; int ch, fd, quiet = 0, i = 0; int pipe_fd[2]; int immediate_daemon = 0; struct passwd *pw; pid_t otherpid; cap_rights_t rights; /* Initially, log errors to stderr as well as to syslogd. */ openlog(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY); setlogmask(LOG_UPTO(LOG_DEBUG)); while ((ch = getopt(argc, argv, "bc:dl:p:qu")) != -1) switch (ch) { case 'b': immediate_daemon = 1; break; case 'c': path_dhclient_conf = optarg; break; case 'd': no_daemon = 1; break; case 'l': path_dhclient_db = optarg; break; case 'p': path_dhclient_pidfile = optarg; break; case 'q': quiet = 1; break; case 'u': unknown_ok = 0; break; default: usage(); } argc -= optind; argv += optind; if (argc != 1) usage(); if (path_dhclient_pidfile == NULL) { asprintf(&path_dhclient_pidfile, "%sdhclient.%s.pid", _PATH_VARRUN, *argv); if (path_dhclient_pidfile == NULL) error("asprintf"); } pidfile = pidfile_open(path_dhclient_pidfile, 0600, &otherpid); if (pidfile == NULL) { if (errno == EEXIST) error("dhclient already running, pid: %d.", otherpid); if (errno == EAGAIN) error("dhclient already running."); warning("Cannot open or create pidfile: %m"); } if ((ifi = calloc(1, sizeof(struct interface_info))) == NULL) error("calloc"); if (strlcpy(ifi->name, argv[0], IFNAMSIZ) >= IFNAMSIZ) error("Interface name too long"); if (path_dhclient_db == NULL && asprintf(&path_dhclient_db, "%s.%s", _PATH_DHCLIENT_DB, ifi->name) == -1) error("asprintf"); if (quiet) log_perror = 0; tzset(); time(&cur_time); inaddr_broadcast.s_addr = INADDR_BROADCAST; inaddr_any.s_addr = INADDR_ANY; read_client_conf(); /* The next bit is potentially very time-consuming, so write out the pidfile right away. We will write it out again with the correct pid after daemonizing. */ if (pidfile != NULL) pidfile_write(pidfile); if (!interface_link_status(ifi->name)) { fprintf(stderr, "%s: no link ...", ifi->name); fflush(stderr); sleep(1); while (!interface_link_status(ifi->name)) { fprintf(stderr, "."); fflush(stderr); if (++i > 10) { fprintf(stderr, " giving up\n"); exit(1); } sleep(1); } fprintf(stderr, " got link\n"); } ifi->linkstat = 1; if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) error("cannot open %s: %m", _PATH_DEVNULL); if ((pw = getpwnam("_dhcp")) == NULL) { warning("no such user: _dhcp, falling back to \"nobody\""); if ((pw = getpwnam("nobody")) == NULL) error("no such user: nobody"); } /* * Obtain hostname before entering capability mode - it won't be * possible then, as reading kern.hostname is not permitted. */ if (gethostname(hostname, sizeof(hostname)) < 0) hostname[0] = '\0'; priv_script_init("PREINIT", NULL); if (ifi->client->alias) priv_script_write_params("alias_", ifi->client->alias); priv_script_go(); /* set up the interface */ discover_interfaces(ifi); if (pipe(pipe_fd) == -1) error("pipe"); fork_privchld(pipe_fd[0], pipe_fd[1]); close(ifi->ufdesc); ifi->ufdesc = -1; close(ifi->wfdesc); ifi->wfdesc = -1; close(pipe_fd[0]); privfd = pipe_fd[1]; cap_rights_init(&rights, CAP_READ, CAP_WRITE); if (cap_rights_limit(privfd, &rights) < 0 && errno != ENOSYS) error("can't limit private descriptor: %m"); if ((fd = open(path_dhclient_db, O_RDONLY|O_EXLOCK|O_CREAT, 0)) == -1) error("can't open and lock %s: %m", path_dhclient_db); read_client_leases(); rewrite_client_leases(); close(fd); if ((routefd = socket(PF_ROUTE, SOCK_RAW, 0)) != -1) add_protocol("AF_ROUTE", routefd, routehandler, ifi); if (shutdown(routefd, SHUT_WR) < 0) error("can't shutdown route socket: %m"); cap_rights_init(&rights, CAP_EVENT, CAP_READ); if (cap_rights_limit(routefd, &rights) < 0 && errno != ENOSYS) error("can't limit route socket: %m"); if (chroot(_PATH_VAREMPTY) == -1) error("chroot"); if (chdir("/") == -1) error("chdir(\"/\")"); if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) || setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) error("can't drop privileges: %m"); endpwent(); setproctitle("%s", ifi->name); if (cap_enter() < 0 && errno != ENOSYS) error("can't enter capability mode: %m"); if (immediate_daemon) go_daemon(); ifi->client->state = S_INIT; state_reboot(ifi); bootp_packet_handler = do_packet; dispatch(); /* not reached */ return (0); } void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-bdqu] ", __progname); fprintf(stderr, "[-c conffile] [-l leasefile] interface\n"); exit(1); } /* * Individual States: * * Each routine is called from the dhclient_state_machine() in one of * these conditions: * -> entering INIT state * -> recvpacket_flag == 0: timeout in this state * -> otherwise: received a packet in this state * * Return conditions as handled by dhclient_state_machine(): * Returns 1, sendpacket_flag = 1: send packet, reset timer. * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone). * Returns 0: finish the nap which was interrupted for no good reason. * * Several per-interface variables are used to keep track of the process: * active_lease: the lease that is being used on the interface * (null pointer if not configured yet). * offered_leases: leases corresponding to DHCPOFFER messages that have * been sent to us by DHCP servers. * acked_leases: leases corresponding to DHCPACK messages that have been * sent to us by DHCP servers. * sendpacket: DHCP packet we're trying to send. * destination: IP address to send sendpacket to * In addition, there are several relevant per-lease variables. * T1_expiry, T2_expiry, lease_expiry: lease milestones * In the active lease, these control the process of renewing the lease; * In leases on the acked_leases list, this simply determines when we * can no longer legitimately use the lease. */ void state_reboot(void *ipp) { struct interface_info *ip = ipp; /* If we don't remember an active lease, go straight to INIT. */ if (!ip->client->active || ip->client->active->is_bootp) { state_init(ip); return; } /* We are in the rebooting state. */ ip->client->state = S_REBOOTING; /* make_request doesn't initialize xid because it normally comes from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER, so pick an xid now. */ ip->client->xid = arc4random(); /* Make a DHCPREQUEST packet, and set appropriate per-interface flags. */ make_request(ip, ip->client->active); ip->client->destination = iaddr_broadcast; ip->client->first_sending = cur_time; ip->client->interval = ip->client->config->initial_interval; /* Zap the medium list... */ ip->client->medium = NULL; /* Send out the first DHCPREQUEST packet. */ send_request(ip); } /* * Called when a lease has completely expired and we've * been unable to renew it. */ void state_init(void *ipp) { struct interface_info *ip = ipp; ASSERT_STATE(state, S_INIT); /* Make a DHCPDISCOVER packet, and set appropriate per-interface flags. */ make_discover(ip, ip->client->active); ip->client->xid = ip->client->packet.xid; ip->client->destination = iaddr_broadcast; ip->client->state = S_SELECTING; ip->client->first_sending = cur_time; ip->client->interval = ip->client->config->initial_interval; /* Add an immediate timeout to cause the first DHCPDISCOVER packet to go out. */ send_discover(ip); } /* * state_selecting is called when one or more DHCPOFFER packets * have been received and a configurable period of time has passed. */ void state_selecting(void *ipp) { struct interface_info *ip = ipp; struct client_lease *lp, *next, *picked; ASSERT_STATE(state, S_SELECTING); /* Cancel state_selecting and send_discover timeouts, since either one could have got us here. */ cancel_timeout(state_selecting, ip); cancel_timeout(send_discover, ip); /* We have received one or more DHCPOFFER packets. Currently, the only criterion by which we judge leases is whether or not we get a response when we arp for them. */ picked = NULL; for (lp = ip->client->offered_leases; lp; lp = next) { next = lp->next; /* Check to see if we got an ARPREPLY for the address in this particular lease. */ if (!picked) { script_init("ARPCHECK", lp->medium); script_write_params("check_", lp); /* If the ARPCHECK code detects another machine using the offered address, it exits nonzero. We need to send a DHCPDECLINE and toss the lease. */ if (script_go()) { make_decline(ip, lp); send_decline(ip); goto freeit; } picked = lp; picked->next = NULL; } else { freeit: free_client_lease(lp); } } ip->client->offered_leases = NULL; /* If we just tossed all the leases we were offered, go back to square one. */ if (!picked) { ip->client->state = S_INIT; state_init(ip); return; } /* If it was a BOOTREPLY, we can just take the address right now. */ if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) { ip->client->new = picked; /* Make up some lease expiry times XXX these should be configurable. */ ip->client->new->expiry = cur_time + 12000; ip->client->new->renewal += cur_time + 8000; ip->client->new->rebind += cur_time + 10000; ip->client->state = S_REQUESTING; /* Bind to the address we received. */ bind_lease(ip); return; } /* Go to the REQUESTING state. */ ip->client->destination = iaddr_broadcast; ip->client->state = S_REQUESTING; ip->client->first_sending = cur_time; ip->client->interval = ip->client->config->initial_interval; /* Make a DHCPREQUEST packet from the lease we picked. */ make_request(ip, picked); ip->client->xid = ip->client->packet.xid; /* Toss the lease we picked - we'll get it back in a DHCPACK. */ free_client_lease(picked); /* Add an immediate timeout to send the first DHCPREQUEST packet. */ send_request(ip); } /* state_requesting is called when we receive a DHCPACK message after having sent out one or more DHCPREQUEST packets. */ void dhcpack(struct packet *packet) { struct interface_info *ip = packet->interface; struct client_lease *lease; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ if (packet->interface->client->xid != packet->raw->xid || (packet->interface->hw_address.hlen != packet->raw->hlen) || (memcmp(packet->interface->hw_address.haddr, packet->raw->chaddr, packet->raw->hlen))) return; if (ip->client->state != S_REBOOTING && ip->client->state != S_REQUESTING && ip->client->state != S_RENEWING && ip->client->state != S_REBINDING) return; note("DHCPACK from %s", piaddr(packet->client_addr)); lease = packet_to_lease(packet); if (!lease) { note("packet_to_lease failed."); return; } ip->client->new = lease; /* Stop resending DHCPREQUEST. */ cancel_timeout(send_request, ip); /* Figure out the lease time. */ if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data) ip->client->new->expiry = getULong( ip->client->new->options[DHO_DHCP_LEASE_TIME].data); else ip->client->new->expiry = default_lease_time; /* A number that looks negative here is really just very large, because the lease expiry offset is unsigned. */ if (ip->client->new->expiry < 0) ip->client->new->expiry = TIME_MAX; /* XXX should be fixed by resetting the client state */ if (ip->client->new->expiry < 60) ip->client->new->expiry = 60; /* Take the server-provided renewal time if there is one; otherwise figure it out according to the spec. */ if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len) ip->client->new->renewal = getULong( ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data); else ip->client->new->renewal = ip->client->new->expiry / 2; /* Same deal with the rebind time. */ if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len) ip->client->new->rebind = getULong( ip->client->new->options[DHO_DHCP_REBINDING_TIME].data); else ip->client->new->rebind = ip->client->new->renewal + ip->client->new->renewal / 2 + ip->client->new->renewal / 4; ip->client->new->expiry += cur_time; /* Lease lengths can never be negative. */ if (ip->client->new->expiry < cur_time) ip->client->new->expiry = TIME_MAX; ip->client->new->renewal += cur_time; if (ip->client->new->renewal < cur_time) ip->client->new->renewal = TIME_MAX; ip->client->new->rebind += cur_time; if (ip->client->new->rebind < cur_time) ip->client->new->rebind = TIME_MAX; bind_lease(ip); } void bind_lease(struct interface_info *ip) { /* Remember the medium. */ ip->client->new->medium = ip->client->medium; /* Write out the new lease. */ write_client_lease(ip, ip->client->new, 0); /* Run the client script with the new parameters. */ script_init((ip->client->state == S_REQUESTING ? "BOUND" : (ip->client->state == S_RENEWING ? "RENEW" : (ip->client->state == S_REBOOTING ? "REBOOT" : "REBIND"))), ip->client->new->medium); if (ip->client->active && ip->client->state != S_REBOOTING) script_write_params("old_", ip->client->active); script_write_params("new_", ip->client->new); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); /* Replace the old active lease with the new one. */ if (ip->client->active) free_client_lease(ip->client->active); ip->client->active = ip->client->new; ip->client->new = NULL; /* Set up a timeout to start the renewal process. */ add_timeout(ip->client->active->renewal, state_bound, ip); note("bound to %s -- renewal in %d seconds.", piaddr(ip->client->active->address), (int)(ip->client->active->renewal - cur_time)); ip->client->state = S_BOUND; reinitialize_interfaces(); go_daemon(); } /* * state_bound is called when we've successfully bound to a particular * lease, but the renewal time on that lease has expired. We are * expected to unicast a DHCPREQUEST to the server that gave us our * original lease. */ void state_bound(void *ipp) { struct interface_info *ip = ipp; ASSERT_STATE(state, S_BOUND); /* T1 has expired. */ make_request(ip, ip->client->active); ip->client->xid = ip->client->packet.xid; if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) { memcpy(ip->client->destination.iabuf, ip->client->active-> options[DHO_DHCP_SERVER_IDENTIFIER].data, 4); ip->client->destination.len = 4; } else ip->client->destination = iaddr_broadcast; ip->client->first_sending = cur_time; ip->client->interval = ip->client->config->initial_interval; ip->client->state = S_RENEWING; /* Send the first packet immediately. */ send_request(ip); } void bootp(struct packet *packet) { struct iaddrlist *ap; if (packet->raw->op != BOOTREPLY) return; /* If there's a reject list, make sure this packet's sender isn't on it. */ for (ap = packet->interface->client->config->reject_list; ap; ap = ap->next) { if (addr_eq(packet->client_addr, ap->addr)) { note("BOOTREPLY from %s rejected.", piaddr(ap->addr)); return; } } dhcpoffer(packet); } void dhcp(struct packet *packet) { struct iaddrlist *ap; void (*handler)(struct packet *); char *type; switch (packet->packet_type) { case DHCPOFFER: handler = dhcpoffer; type = "DHCPOFFER"; break; case DHCPNAK: handler = dhcpnak; type = "DHCPNACK"; break; case DHCPACK: handler = dhcpack; type = "DHCPACK"; break; default: return; } /* If there's a reject list, make sure this packet's sender isn't on it. */ for (ap = packet->interface->client->config->reject_list; ap; ap = ap->next) { if (addr_eq(packet->client_addr, ap->addr)) { note("%s from %s rejected.", type, piaddr(ap->addr)); return; } } (*handler)(packet); } void dhcpoffer(struct packet *packet) { struct interface_info *ip = packet->interface; struct client_lease *lease, *lp; int i; int arp_timeout_needed, stop_selecting; char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ? "DHCPOFFER" : "BOOTREPLY"; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ if (ip->client->state != S_SELECTING || packet->interface->client->xid != packet->raw->xid || (packet->interface->hw_address.hlen != packet->raw->hlen) || (memcmp(packet->interface->hw_address.haddr, packet->raw->chaddr, packet->raw->hlen))) return; note("%s from %s", name, piaddr(packet->client_addr)); /* If this lease doesn't supply the minimum required parameters, blow it off. */ for (i = 0; ip->client->config->required_options[i]; i++) { if (!packet->options[ip->client->config-> required_options[i]].len) { note("%s isn't satisfactory.", name); return; } } /* If we've already seen this lease, don't record it again. */ for (lease = ip->client->offered_leases; lease; lease = lease->next) { if (lease->address.len == sizeof(packet->raw->yiaddr) && !memcmp(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len)) { debug("%s already seen.", name); return; } } lease = packet_to_lease(packet); if (!lease) { note("packet_to_lease failed."); return; } /* If this lease was acquired through a BOOTREPLY, record that fact. */ if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len) lease->is_bootp = 1; /* Record the medium under which this lease was offered. */ lease->medium = ip->client->medium; /* Send out an ARP Request for the offered IP address. */ script_init("ARPSEND", lease->medium); script_write_params("check_", lease); /* If the script can't send an ARP request without waiting, we'll be waiting when we do the ARPCHECK, so don't wait now. */ if (script_go()) arp_timeout_needed = 0; else arp_timeout_needed = 2; /* Figure out when we're supposed to stop selecting. */ stop_selecting = ip->client->first_sending + ip->client->config->select_interval; /* If this is the lease we asked for, put it at the head of the list, and don't mess with the arp request timeout. */ if (lease->address.len == ip->client->requested_address.len && !memcmp(lease->address.iabuf, ip->client->requested_address.iabuf, ip->client->requested_address.len)) { lease->next = ip->client->offered_leases; ip->client->offered_leases = lease; } else { /* If we already have an offer, and arping for this offer would take us past the selection timeout, then don't extend the timeout - just hope for the best. */ if (ip->client->offered_leases && (cur_time + arp_timeout_needed) > stop_selecting) arp_timeout_needed = 0; /* Put the lease at the end of the list. */ lease->next = NULL; if (!ip->client->offered_leases) ip->client->offered_leases = lease; else { for (lp = ip->client->offered_leases; lp->next; lp = lp->next) ; /* nothing */ lp->next = lease; } } /* If we're supposed to stop selecting before we've had time to wait for the ARPREPLY, add some delay to wait for the ARPREPLY. */ if (stop_selecting - cur_time < arp_timeout_needed) stop_selecting = cur_time + arp_timeout_needed; /* If the selecting interval has expired, go immediately to state_selecting(). Otherwise, time out into state_selecting at the select interval. */ if (stop_selecting <= 0) state_selecting(ip); else { add_timeout(stop_selecting, state_selecting, ip); cancel_timeout(send_discover, ip); } } /* Allocate a client_lease structure and initialize it from the parameters in the specified packet. */ struct client_lease * packet_to_lease(struct packet *packet) { struct client_lease *lease; int i; lease = malloc(sizeof(struct client_lease)); if (!lease) { warning("dhcpoffer: no memory to record lease."); return (NULL); } memset(lease, 0, sizeof(*lease)); /* Copy the lease options. */ for (i = 0; i < 256; i++) { if (packet->options[i].len) { lease->options[i].data = malloc(packet->options[i].len + 1); if (!lease->options[i].data) { warning("dhcpoffer: no memory for option %d", i); free_client_lease(lease); return (NULL); } else { memcpy(lease->options[i].data, packet->options[i].data, packet->options[i].len); lease->options[i].len = packet->options[i].len; lease->options[i].data[lease->options[i].len] = 0; } if (!check_option(lease,i)) { /* ignore a bogus lease offer */ warning("Invalid lease option - ignoring offer"); free_client_lease(lease); return (NULL); } } } lease->address.len = sizeof(packet->raw->yiaddr); memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len); lease->nextserver.len = sizeof(packet->raw->siaddr); memcpy(lease->nextserver.iabuf, &packet->raw->siaddr, lease->nextserver.len); /* If the server name was filled out, copy it. Do not attempt to validate the server name as a host name. RFC 2131 merely states that sname is NUL-terminated (which do do not assume) and that it is the server's host name. Since the ISC client and server allow arbitrary characters, we do as well. */ if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) && packet->raw->sname[0]) { lease->server_name = malloc(DHCP_SNAME_LEN + 1); if (!lease->server_name) { warning("dhcpoffer: no memory for server name."); free_client_lease(lease); return (NULL); } memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN); lease->server_name[DHCP_SNAME_LEN]='\0'; } /* Ditto for the filename. */ if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len || !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) && packet->raw->file[0]) { /* Don't count on the NUL terminator. */ lease->filename = malloc(DHCP_FILE_LEN + 1); if (!lease->filename) { warning("dhcpoffer: no memory for filename."); free_client_lease(lease); return (NULL); } memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN); lease->filename[DHCP_FILE_LEN]='\0'; } return lease; } void dhcpnak(struct packet *packet) { struct interface_info *ip = packet->interface; /* If we're not receptive to an offer right now, or if the offer has an unrecognizable transaction id, then just drop it. */ if (packet->interface->client->xid != packet->raw->xid || (packet->interface->hw_address.hlen != packet->raw->hlen) || (memcmp(packet->interface->hw_address.haddr, packet->raw->chaddr, packet->raw->hlen))) return; if (ip->client->state != S_REBOOTING && ip->client->state != S_REQUESTING && ip->client->state != S_RENEWING && ip->client->state != S_REBINDING) return; note("DHCPNAK from %s", piaddr(packet->client_addr)); if (!ip->client->active) { note("DHCPNAK with no active lease.\n"); return; } free_client_lease(ip->client->active); ip->client->active = NULL; /* Stop sending DHCPREQUEST packets... */ cancel_timeout(send_request, ip); ip->client->state = S_INIT; state_init(ip); } /* Send out a DHCPDISCOVER packet, and set a timeout to send out another one after the right interval has expired. If we don't get an offer by the time we reach the panic interval, call the panic function. */ void send_discover(void *ipp) { struct interface_info *ip = ipp; int interval, increase = 1; /* Figure out how long it's been since we started transmitting. */ interval = cur_time - ip->client->first_sending; /* If we're past the panic timeout, call the script and tell it we haven't found anything for this interface yet. */ if (interval > ip->client->config->timeout) { state_panic(ip); return; } /* If we're selecting media, try the whole list before doing the exponential backoff, but if we've already received an offer, stop looping, because we obviously have it right. */ if (!ip->client->offered_leases && ip->client->config->media) { int fail = 0; again: if (ip->client->medium) { ip->client->medium = ip->client->medium->next; increase = 0; } if (!ip->client->medium) { if (fail) error("No valid media types for %s!", ip->name); ip->client->medium = ip->client->config->media; increase = 1; } note("Trying medium \"%s\" %d", ip->client->medium->string, increase); script_init("MEDIUM", ip->client->medium); if (script_go()) goto again; } /* * If we're supposed to increase the interval, do so. If it's * currently zero (i.e., we haven't sent any packets yet), set * it to one; otherwise, add to it a random number between zero * and two times itself. On average, this means that it will * double with every transmission. */ if (increase) { if (!ip->client->interval) ip->client->interval = ip->client->config->initial_interval; else { ip->client->interval += (arc4random() >> 2) % (2 * ip->client->interval); } /* Don't backoff past cutoff. */ if (ip->client->interval > ip->client->config->backoff_cutoff) ip->client->interval = ((ip->client->config->backoff_cutoff / 2) + ((arc4random() >> 2) % ip->client->config->backoff_cutoff)); } else if (!ip->client->interval) ip->client->interval = ip->client->config->initial_interval; /* If the backoff would take us to the panic timeout, just use that as the interval. */ if (cur_time + ip->client->interval > ip->client->first_sending + ip->client->config->timeout) ip->client->interval = (ip->client->first_sending + ip->client->config->timeout) - cur_time + 1; /* Record the number of seconds since we started sending. */ if (interval < 65536) ip->client->packet.secs = htons(interval); else ip->client->packet.secs = htons(65535); ip->client->secs = ip->client->packet.secs; note("DHCPDISCOVER on %s to %s port %d interval %d", ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT, (int)ip->client->interval); /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, inaddr_any, inaddr_broadcast); add_timeout(cur_time + ip->client->interval, send_discover, ip); } /* * state_panic gets called if we haven't received any offers in a preset * amount of time. When this happens, we try to use existing leases * that haven't yet expired, and failing that, we call the client script * and hope it can do something. */ void state_panic(void *ipp) { struct interface_info *ip = ipp; struct client_lease *loop = ip->client->active; struct client_lease *lp; note("No DHCPOFFERS received."); /* We may not have an active lease, but we may have some predefined leases that we can try. */ if (!ip->client->active && ip->client->leases) goto activate_next; /* Run through the list of leases and see if one can be used. */ while (ip->client->active) { if (ip->client->active->expiry > cur_time) { note("Trying recorded lease %s", piaddr(ip->client->active->address)); /* Run the client script with the existing parameters. */ script_init("TIMEOUT", ip->client->active->medium); script_write_params("new_", ip->client->active); if (ip->client->alias) script_write_params("alias_", ip->client->alias); /* If the old lease is still good and doesn't yet need renewal, go into BOUND state and timeout at the renewal time. */ if (!script_go()) { if (cur_time < ip->client->active->renewal) { ip->client->state = S_BOUND; note("bound: renewal in %d seconds.", (int)(ip->client->active->renewal - cur_time)); add_timeout( ip->client->active->renewal, state_bound, ip); } else { ip->client->state = S_BOUND; note("bound: immediate renewal."); state_bound(ip); } reinitialize_interfaces(); go_daemon(); return; } } /* If there are no other leases, give up. */ if (!ip->client->leases) { ip->client->leases = ip->client->active; ip->client->active = NULL; break; } activate_next: /* Otherwise, put the active lease at the end of the lease list, and try another lease.. */ for (lp = ip->client->leases; lp->next; lp = lp->next) ; lp->next = ip->client->active; if (lp->next) lp->next->next = NULL; ip->client->active = ip->client->leases; ip->client->leases = ip->client->leases->next; /* If we already tried this lease, we've exhausted the set of leases, so we might as well give up for now. */ if (ip->client->active == loop) break; else if (!loop) loop = ip->client->active; } /* No leases were available, or what was available didn't work, so tell the shell script that we failed to allocate an address, and try again later. */ note("No working leases in persistent database - sleeping.\n"); script_init("FAIL", NULL); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); ip->client->state = S_INIT; add_timeout(cur_time + ip->client->config->retry_interval, state_init, ip); go_daemon(); } void send_request(void *ipp) { struct interface_info *ip = ipp; struct in_addr from, to; int interval; /* Figure out how long it's been since we started transmitting. */ interval = cur_time - ip->client->first_sending; /* If we're in the INIT-REBOOT or REQUESTING state and we're past the reboot timeout, go to INIT and see if we can DISCOVER an address... */ /* XXX In the INIT-REBOOT state, if we don't get an ACK, it means either that we're on a network with no DHCP server, or that our server is down. In the latter case, assuming that there is a backup DHCP server, DHCPDISCOVER will get us a new address, but we could also have successfully reused our old address. In the former case, we're hosed anyway. This is not a win-prone situation. */ if ((ip->client->state == S_REBOOTING || ip->client->state == S_REQUESTING) && interval > ip->client->config->reboot_timeout) { cancel: ip->client->state = S_INIT; cancel_timeout(send_request, ip); state_init(ip); return; } /* If we're in the reboot state, make sure the media is set up correctly. */ if (ip->client->state == S_REBOOTING && !ip->client->medium && ip->client->active->medium ) { script_init("MEDIUM", ip->client->active->medium); /* If the medium we chose won't fly, go to INIT state. */ if (script_go()) goto cancel; /* Record the medium. */ ip->client->medium = ip->client->active->medium; } /* If the lease has expired, relinquish the address and go back to the INIT state. */ if (ip->client->state != S_REQUESTING && cur_time > ip->client->active->expiry) { /* Run the client script with the new parameters. */ script_init("EXPIRE", NULL); script_write_params("old_", ip->client->active); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); /* Now do a preinit on the interface so that we can discover a new address. */ script_init("PREINIT", NULL); if (ip->client->alias) script_write_params("alias_", ip->client->alias); script_go(); ip->client->state = S_INIT; state_init(ip); return; } /* Do the exponential backoff... */ if (!ip->client->interval) ip->client->interval = ip->client->config->initial_interval; else ip->client->interval += ((arc4random() >> 2) % (2 * ip->client->interval)); /* Don't backoff past cutoff. */ if (ip->client->interval > ip->client->config->backoff_cutoff) ip->client->interval = ((ip->client->config->backoff_cutoff / 2) + ((arc4random() >> 2) % ip->client->interval)); /* If the backoff would take us to the expiry time, just set the timeout to the expiry time. */ if (ip->client->state != S_REQUESTING && cur_time + ip->client->interval > ip->client->active->expiry) ip->client->interval = ip->client->active->expiry - cur_time + 1; /* If the lease T2 time has elapsed, or if we're not yet bound, broadcast the DHCPREQUEST rather than unicasting. */ if (ip->client->state == S_REQUESTING || ip->client->state == S_REBOOTING || cur_time > ip->client->active->rebind) to.s_addr = INADDR_BROADCAST; else memcpy(&to.s_addr, ip->client->destination.iabuf, sizeof(to.s_addr)); if (ip->client->state != S_REQUESTING) memcpy(&from, ip->client->active->address.iabuf, sizeof(from)); else from.s_addr = INADDR_ANY; /* Record the number of seconds since we started sending. */ if (ip->client->state == S_REQUESTING) ip->client->packet.secs = ip->client->secs; else { if (interval < 65536) ip->client->packet.secs = htons(interval); else ip->client->packet.secs = htons(65535); } note("DHCPREQUEST on %s to %s port %d", ip->name, inet_ntoa(to), REMOTE_PORT); /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, from, to); add_timeout(cur_time + ip->client->interval, send_request, ip); } void send_decline(void *ipp) { struct interface_info *ip = ipp; note("DHCPDECLINE on %s to %s port %d", ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT); /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, inaddr_any, inaddr_broadcast); } void make_discover(struct interface_info *ip, struct client_lease *lease) { unsigned char discover = DHCPDISCOVER; struct tree_cache *options[256]; struct tree_cache option_elements[256]; int i; memset(option_elements, 0, sizeof(option_elements)); memset(options, 0, sizeof(options)); memset(&ip->client->packet, 0, sizeof(ip->client->packet)); /* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */ i = DHO_DHCP_MESSAGE_TYPE; options[i] = &option_elements[i]; options[i]->value = &discover; options[i]->len = sizeof(discover); options[i]->buf_size = sizeof(discover); options[i]->timeout = 0xFFFFFFFF; /* Request the options we want */ i = DHO_DHCP_PARAMETER_REQUEST_LIST; options[i] = &option_elements[i]; options[i]->value = ip->client->config->requested_options; options[i]->len = ip->client->config->requested_option_count; options[i]->buf_size = ip->client->config->requested_option_count; options[i]->timeout = 0xFFFFFFFF; /* If we had an address, try to get it again. */ if (lease) { ip->client->requested_address = lease->address; i = DHO_DHCP_REQUESTED_ADDRESS; options[i] = &option_elements[i]; options[i]->value = lease->address.iabuf; options[i]->len = lease->address.len; options[i]->buf_size = lease->address.len; options[i]->timeout = 0xFFFFFFFF; } else ip->client->requested_address.len = 0; /* Send any options requested in the config file. */ for (i = 0; i < 256; i++) if (!options[i] && ip->client->config->send_options[i].data) { options[i] = &option_elements[i]; options[i]->value = ip->client->config->send_options[i].data; options[i]->len = ip->client->config->send_options[i].len; options[i]->buf_size = ip->client->config->send_options[i].len; options[i]->timeout = 0xFFFFFFFF; } /* send host name if not set via config file. */ if (!options[DHO_HOST_NAME]) { if (hostname[0] != '\0') { size_t len; char* posDot = strchr(hostname, '.'); if (posDot != NULL) len = posDot - hostname; else len = strlen(hostname); options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; options[DHO_HOST_NAME]->value = hostname; options[DHO_HOST_NAME]->len = len; options[DHO_HOST_NAME]->buf_size = len; options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; } } /* set unique client identifier */ char client_ident[sizeof(struct hardware)]; if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? ip->hw_address.hlen : sizeof(client_ident)-1; client_ident[0] = ip->hw_address.htype; memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; } /* Set up the option buffer... */ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, options, 0, 0, 0, NULL, 0); if (ip->client->packet_length < BOOTP_MIN_LEN) ip->client->packet_length = BOOTP_MIN_LEN; ip->client->packet.op = BOOTREQUEST; ip->client->packet.htype = ip->hw_address.htype; ip->client->packet.hlen = ip->hw_address.hlen; ip->client->packet.hops = 0; ip->client->packet.xid = arc4random(); ip->client->packet.secs = 0; /* filled in by send_discover. */ ip->client->packet.flags = 0; memset(&(ip->client->packet.ciaddr), 0, sizeof(ip->client->packet.ciaddr)); memset(&(ip->client->packet.yiaddr), 0, sizeof(ip->client->packet.yiaddr)); memset(&(ip->client->packet.siaddr), 0, sizeof(ip->client->packet.siaddr)); memset(&(ip->client->packet.giaddr), 0, sizeof(ip->client->packet.giaddr)); memcpy(ip->client->packet.chaddr, ip->hw_address.haddr, ip->hw_address.hlen); } void make_request(struct interface_info *ip, struct client_lease * lease) { unsigned char request = DHCPREQUEST; struct tree_cache *options[256]; struct tree_cache option_elements[256]; int i; memset(options, 0, sizeof(options)); memset(&ip->client->packet, 0, sizeof(ip->client->packet)); /* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */ i = DHO_DHCP_MESSAGE_TYPE; options[i] = &option_elements[i]; options[i]->value = &request; options[i]->len = sizeof(request); options[i]->buf_size = sizeof(request); options[i]->timeout = 0xFFFFFFFF; /* Request the options we want */ i = DHO_DHCP_PARAMETER_REQUEST_LIST; options[i] = &option_elements[i]; options[i]->value = ip->client->config->requested_options; options[i]->len = ip->client->config->requested_option_count; options[i]->buf_size = ip->client->config->requested_option_count; options[i]->timeout = 0xFFFFFFFF; /* If we are requesting an address that hasn't yet been assigned to us, use the DHCP Requested Address option. */ if (ip->client->state == S_REQUESTING) { /* Send back the server identifier... */ i = DHO_DHCP_SERVER_IDENTIFIER; options[i] = &option_elements[i]; options[i]->value = lease->options[i].data; options[i]->len = lease->options[i].len; options[i]->buf_size = lease->options[i].len; options[i]->timeout = 0xFFFFFFFF; } if (ip->client->state == S_REQUESTING || ip->client->state == S_REBOOTING) { ip->client->requested_address = lease->address; i = DHO_DHCP_REQUESTED_ADDRESS; options[i] = &option_elements[i]; options[i]->value = lease->address.iabuf; options[i]->len = lease->address.len; options[i]->buf_size = lease->address.len; options[i]->timeout = 0xFFFFFFFF; } else ip->client->requested_address.len = 0; /* Send any options requested in the config file. */ for (i = 0; i < 256; i++) if (!options[i] && ip->client->config->send_options[i].data) { options[i] = &option_elements[i]; options[i]->value = ip->client->config->send_options[i].data; options[i]->len = ip->client->config->send_options[i].len; options[i]->buf_size = ip->client->config->send_options[i].len; options[i]->timeout = 0xFFFFFFFF; } /* send host name if not set via config file. */ if (!options[DHO_HOST_NAME]) { if (hostname[0] != '\0') { size_t len; char* posDot = strchr(hostname, '.'); if (posDot != NULL) len = posDot - hostname; else len = strlen(hostname); options[DHO_HOST_NAME] = &option_elements[DHO_HOST_NAME]; options[DHO_HOST_NAME]->value = hostname; options[DHO_HOST_NAME]->len = len; options[DHO_HOST_NAME]->buf_size = len; options[DHO_HOST_NAME]->timeout = 0xFFFFFFFF; } } /* set unique client identifier */ char client_ident[sizeof(struct hardware)]; if (!options[DHO_DHCP_CLIENT_IDENTIFIER]) { int hwlen = (ip->hw_address.hlen < sizeof(client_ident)-1) ? ip->hw_address.hlen : sizeof(client_ident)-1; client_ident[0] = ip->hw_address.htype; memcpy(&client_ident[1], ip->hw_address.haddr, hwlen); options[DHO_DHCP_CLIENT_IDENTIFIER] = &option_elements[DHO_DHCP_CLIENT_IDENTIFIER]; options[DHO_DHCP_CLIENT_IDENTIFIER]->value = client_ident; options[DHO_DHCP_CLIENT_IDENTIFIER]->len = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->buf_size = hwlen+1; options[DHO_DHCP_CLIENT_IDENTIFIER]->timeout = 0xFFFFFFFF; } /* Set up the option buffer... */ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, options, 0, 0, 0, NULL, 0); if (ip->client->packet_length < BOOTP_MIN_LEN) ip->client->packet_length = BOOTP_MIN_LEN; ip->client->packet.op = BOOTREQUEST; ip->client->packet.htype = ip->hw_address.htype; ip->client->packet.hlen = ip->hw_address.hlen; ip->client->packet.hops = 0; ip->client->packet.xid = ip->client->xid; ip->client->packet.secs = 0; /* Filled in by send_request. */ /* If we own the address we're requesting, put it in ciaddr; otherwise set ciaddr to zero. */ if (ip->client->state == S_BOUND || ip->client->state == S_RENEWING || ip->client->state == S_REBINDING) { memcpy(&ip->client->packet.ciaddr, lease->address.iabuf, lease->address.len); ip->client->packet.flags = 0; } else { memset(&ip->client->packet.ciaddr, 0, sizeof(ip->client->packet.ciaddr)); ip->client->packet.flags = 0; } memset(&ip->client->packet.yiaddr, 0, sizeof(ip->client->packet.yiaddr)); memset(&ip->client->packet.siaddr, 0, sizeof(ip->client->packet.siaddr)); memset(&ip->client->packet.giaddr, 0, sizeof(ip->client->packet.giaddr)); memcpy(ip->client->packet.chaddr, ip->hw_address.haddr, ip->hw_address.hlen); } void make_decline(struct interface_info *ip, struct client_lease *lease) { struct tree_cache *options[256], message_type_tree; struct tree_cache requested_address_tree; struct tree_cache server_id_tree, client_id_tree; unsigned char decline = DHCPDECLINE; int i; memset(options, 0, sizeof(options)); memset(&ip->client->packet, 0, sizeof(ip->client->packet)); /* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */ i = DHO_DHCP_MESSAGE_TYPE; options[i] = &message_type_tree; options[i]->value = &decline; options[i]->len = sizeof(decline); options[i]->buf_size = sizeof(decline); options[i]->timeout = 0xFFFFFFFF; /* Send back the server identifier... */ i = DHO_DHCP_SERVER_IDENTIFIER; options[i] = &server_id_tree; options[i]->value = lease->options[i].data; options[i]->len = lease->options[i].len; options[i]->buf_size = lease->options[i].len; options[i]->timeout = 0xFFFFFFFF; /* Send back the address we're declining. */ i = DHO_DHCP_REQUESTED_ADDRESS; options[i] = &requested_address_tree; options[i]->value = lease->address.iabuf; options[i]->len = lease->address.len; options[i]->buf_size = lease->address.len; options[i]->timeout = 0xFFFFFFFF; /* Send the uid if the user supplied one. */ i = DHO_DHCP_CLIENT_IDENTIFIER; if (ip->client->config->send_options[i].len) { options[i] = &client_id_tree; options[i]->value = ip->client->config->send_options[i].data; options[i]->len = ip->client->config->send_options[i].len; options[i]->buf_size = ip->client->config->send_options[i].len; options[i]->timeout = 0xFFFFFFFF; } /* Set up the option buffer... */ ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0, options, 0, 0, 0, NULL, 0); if (ip->client->packet_length < BOOTP_MIN_LEN) ip->client->packet_length = BOOTP_MIN_LEN; ip->client->packet.op = BOOTREQUEST; ip->client->packet.htype = ip->hw_address.htype; ip->client->packet.hlen = ip->hw_address.hlen; ip->client->packet.hops = 0; ip->client->packet.xid = ip->client->xid; ip->client->packet.secs = 0; /* Filled in by send_request. */ ip->client->packet.flags = 0; /* ciaddr must always be zero. */ memset(&ip->client->packet.ciaddr, 0, sizeof(ip->client->packet.ciaddr)); memset(&ip->client->packet.yiaddr, 0, sizeof(ip->client->packet.yiaddr)); memset(&ip->client->packet.siaddr, 0, sizeof(ip->client->packet.siaddr)); memset(&ip->client->packet.giaddr, 0, sizeof(ip->client->packet.giaddr)); memcpy(ip->client->packet.chaddr, ip->hw_address.haddr, ip->hw_address.hlen); } void free_client_lease(struct client_lease *lease) { int i; if (lease->server_name) free(lease->server_name); if (lease->filename) free(lease->filename); for (i = 0; i < 256; i++) { if (lease->options[i].len) free(lease->options[i].data); } free(lease); } FILE *leaseFile; void rewrite_client_leases(void) { struct client_lease *lp; cap_rights_t rights; if (!leaseFile) { leaseFile = fopen(path_dhclient_db, "w"); if (!leaseFile) error("can't create %s: %m", path_dhclient_db); cap_rights_init(&rights, CAP_FSTAT, CAP_FSYNC, CAP_FTRUNCATE, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(fileno(leaseFile), &rights) < 0 && errno != ENOSYS) { error("can't limit lease descriptor: %m"); } } else { fflush(leaseFile); rewind(leaseFile); } for (lp = ifi->client->leases; lp; lp = lp->next) write_client_lease(ifi, lp, 1); if (ifi->client->active) write_client_lease(ifi, ifi->client->active, 1); fflush(leaseFile); ftruncate(fileno(leaseFile), ftello(leaseFile)); fsync(fileno(leaseFile)); } void write_client_lease(struct interface_info *ip, struct client_lease *lease, int rewrite) { static int leases_written; struct tm *t; int i; if (!rewrite) { if (leases_written++ > 20) { rewrite_client_leases(); leases_written = 0; } } /* If the lease came from the config file, we don't need to stash a copy in the lease database. */ if (lease->is_static) return; if (!leaseFile) { /* XXX */ leaseFile = fopen(path_dhclient_db, "w"); if (!leaseFile) error("can't create %s: %m", path_dhclient_db); } fprintf(leaseFile, "lease {\n"); if (lease->is_bootp) fprintf(leaseFile, " bootp;\n"); fprintf(leaseFile, " interface \"%s\";\n", ip->name); fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address)); if (lease->nextserver.len == sizeof(inaddr_any) && 0 != memcmp(lease->nextserver.iabuf, &inaddr_any, sizeof(inaddr_any))) fprintf(leaseFile, " next-server %s;\n", piaddr(lease->nextserver)); if (lease->filename) fprintf(leaseFile, " filename \"%s\";\n", lease->filename); if (lease->server_name) fprintf(leaseFile, " server-name \"%s\";\n", lease->server_name); if (lease->medium) fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string); for (i = 0; i < 256; i++) if (lease->options[i].len) fprintf(leaseFile, " option %s %s;\n", dhcp_options[i].name, pretty_print_option(i, lease->options[i].data, lease->options[i].len, 1, 1)); t = gmtime(&lease->renewal); fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n", t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); t = gmtime(&lease->rebind); fprintf(leaseFile, " rebind %d %d/%d/%d %02d:%02d:%02d;\n", t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); t = gmtime(&lease->expiry); fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n", t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); fprintf(leaseFile, "}\n"); fflush(leaseFile); } void script_init(char *reason, struct string_list *medium) { size_t len, mediumlen = 0; struct imsg_hdr hdr; struct buf *buf; int errs; if (medium != NULL && medium->string != NULL) mediumlen = strlen(medium->string); hdr.code = IMSG_SCRIPT_INIT; hdr.len = sizeof(struct imsg_hdr) + sizeof(size_t) + mediumlen + sizeof(size_t) + strlen(reason); if ((buf = buf_open(hdr.len)) == NULL) error("buf_open: %m"); errs = 0; errs += buf_add(buf, &hdr, sizeof(hdr)); errs += buf_add(buf, &mediumlen, sizeof(mediumlen)); if (mediumlen > 0) errs += buf_add(buf, medium->string, mediumlen); len = strlen(reason); errs += buf_add(buf, &len, sizeof(len)); errs += buf_add(buf, reason, len); if (errs) error("buf_add: %m"); if (buf_close(privfd, buf) == -1) error("buf_close: %m"); } void priv_script_init(char *reason, char *medium) { struct interface_info *ip = ifi; if (ip) { ip->client->scriptEnvsize = 100; if (ip->client->scriptEnv == NULL) ip->client->scriptEnv = malloc(ip->client->scriptEnvsize * sizeof(char *)); if (ip->client->scriptEnv == NULL) error("script_init: no memory for environment"); ip->client->scriptEnv[0] = strdup(CLIENT_PATH); if (ip->client->scriptEnv[0] == NULL) error("script_init: no memory for environment"); ip->client->scriptEnv[1] = NULL; script_set_env(ip->client, "", "interface", ip->name); if (medium) script_set_env(ip->client, "", "medium", medium); script_set_env(ip->client, "", "reason", reason); } } void priv_script_write_params(char *prefix, struct client_lease *lease) { struct interface_info *ip = ifi; u_int8_t dbuf[1500], *dp = NULL; int i, len; char tbuf[128]; script_set_env(ip->client, prefix, "ip_address", piaddr(lease->address)); if (ip->client->config->default_actions[DHO_SUBNET_MASK] == ACTION_SUPERSEDE) { dp = ip->client->config->defaults[DHO_SUBNET_MASK].data; len = ip->client->config->defaults[DHO_SUBNET_MASK].len; } else { dp = lease->options[DHO_SUBNET_MASK].data; len = lease->options[DHO_SUBNET_MASK].len; } if (len && (len < sizeof(lease->address.iabuf))) { struct iaddr netmask, subnet, broadcast; memcpy(netmask.iabuf, dp, len); netmask.len = len; subnet = subnet_number(lease->address, netmask); if (subnet.len) { script_set_env(ip->client, prefix, "network_number", piaddr(subnet)); if (!lease->options[DHO_BROADCAST_ADDRESS].len) { broadcast = broadcast_addr(subnet, netmask); if (broadcast.len) script_set_env(ip->client, prefix, "broadcast_address", piaddr(broadcast)); } } } if (lease->filename) script_set_env(ip->client, prefix, "filename", lease->filename); if (lease->server_name) script_set_env(ip->client, prefix, "server_name", lease->server_name); for (i = 0; i < 256; i++) { len = 0; if (ip->client->config->defaults[i].len) { if (lease->options[i].len) { switch ( ip->client->config->default_actions[i]) { case ACTION_DEFAULT: dp = lease->options[i].data; len = lease->options[i].len; break; case ACTION_SUPERSEDE: supersede: dp = ip->client-> config->defaults[i].data; len = ip->client-> config->defaults[i].len; break; case ACTION_PREPEND: len = ip->client-> config->defaults[i].len + lease->options[i].len; if (len >= sizeof(dbuf)) { warning("no space to %s %s", "prepend option", dhcp_options[i].name); goto supersede; } dp = dbuf; memcpy(dp, ip->client-> config->defaults[i].data, ip->client-> config->defaults[i].len); memcpy(dp + ip->client-> config->defaults[i].len, lease->options[i].data, lease->options[i].len); dp[len] = '\0'; break; case ACTION_APPEND: /* * When we append, we assume that we're * appending to text. Some MS servers * include a NUL byte at the end of * the search string provided. */ len = ip->client-> config->defaults[i].len + lease->options[i].len; if (len >= sizeof(dbuf)) { warning("no space to %s %s", "append option", dhcp_options[i].name); goto supersede; } memcpy(dbuf, lease->options[i].data, lease->options[i].len); for (dp = dbuf + lease->options[i].len; dp > dbuf; dp--, len--) if (dp[-1] != '\0') break; memcpy(dp, ip->client-> config->defaults[i].data, ip->client-> config->defaults[i].len); dp = dbuf; dp[len] = '\0'; } } else { dp = ip->client-> config->defaults[i].data; len = ip->client-> config->defaults[i].len; } } else if (lease->options[i].len) { len = lease->options[i].len; dp = lease->options[i].data; } else { len = 0; } if (len) { char name[256]; if (dhcp_option_ev_name(name, sizeof(name), &dhcp_options[i])) script_set_env(ip->client, prefix, name, pretty_print_option(i, dp, len, 0, 0)); } } snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry); script_set_env(ip->client, prefix, "expiry", tbuf); } void script_write_params(char *prefix, struct client_lease *lease) { size_t fn_len = 0, sn_len = 0, pr_len = 0; struct imsg_hdr hdr; struct buf *buf; int errs, i; if (lease->filename != NULL) fn_len = strlen(lease->filename); if (lease->server_name != NULL) sn_len = strlen(lease->server_name); if (prefix != NULL) pr_len = strlen(prefix); hdr.code = IMSG_SCRIPT_WRITE_PARAMS; hdr.len = sizeof(hdr) + sizeof(struct client_lease) + sizeof(size_t) + fn_len + sizeof(size_t) + sn_len + sizeof(size_t) + pr_len; for (i = 0; i < 256; i++) hdr.len += sizeof(int) + lease->options[i].len; scripttime = time(NULL); if ((buf = buf_open(hdr.len)) == NULL) error("buf_open: %m"); errs = 0; errs += buf_add(buf, &hdr, sizeof(hdr)); errs += buf_add(buf, lease, sizeof(struct client_lease)); errs += buf_add(buf, &fn_len, sizeof(fn_len)); errs += buf_add(buf, lease->filename, fn_len); errs += buf_add(buf, &sn_len, sizeof(sn_len)); errs += buf_add(buf, lease->server_name, sn_len); errs += buf_add(buf, &pr_len, sizeof(pr_len)); errs += buf_add(buf, prefix, pr_len); for (i = 0; i < 256; i++) { errs += buf_add(buf, &lease->options[i].len, sizeof(lease->options[i].len)); errs += buf_add(buf, lease->options[i].data, lease->options[i].len); } if (errs) error("buf_add: %m"); if (buf_close(privfd, buf) == -1) error("buf_close: %m"); } int script_go(void) { struct imsg_hdr hdr; struct buf *buf; int ret; hdr.code = IMSG_SCRIPT_GO; hdr.len = sizeof(struct imsg_hdr); if ((buf = buf_open(hdr.len)) == NULL) error("buf_open: %m"); if (buf_add(buf, &hdr, sizeof(hdr))) error("buf_add: %m"); if (buf_close(privfd, buf) == -1) error("buf_close: %m"); bzero(&hdr, sizeof(hdr)); buf_read(privfd, &hdr, sizeof(hdr)); if (hdr.code != IMSG_SCRIPT_GO_RET) error("unexpected msg type %u", hdr.code); if (hdr.len != sizeof(hdr) + sizeof(int)) error("received corrupted message"); buf_read(privfd, &ret, sizeof(ret)); scripttime = time(NULL); return (ret); } int priv_script_go(void) { char *scriptName, *argv[2], **envp, *epp[3], reason[] = "REASON=NBI"; static char client_path[] = CLIENT_PATH; struct interface_info *ip = ifi; int pid, wpid, wstatus; scripttime = time(NULL); if (ip) { scriptName = ip->client->config->script_name; envp = ip->client->scriptEnv; } else { scriptName = top_level_config.script_name; epp[0] = reason; epp[1] = client_path; epp[2] = NULL; envp = epp; } argv[0] = scriptName; argv[1] = NULL; pid = fork(); if (pid < 0) { error("fork: %m"); wstatus = 0; } else if (pid) { do { wpid = wait(&wstatus); } while (wpid != pid && wpid > 0); if (wpid < 0) { error("wait: %m"); wstatus = 0; } } else { execve(scriptName, argv, envp); error("execve (%s, ...): %m", scriptName); } if (ip) script_flush_env(ip->client); return (wstatus & 0xff); } void script_set_env(struct client_state *client, const char *prefix, const char *name, const char *value) { int i, j, namelen; namelen = strlen(name); for (i = 0; client->scriptEnv[i]; i++) if (strncmp(client->scriptEnv[i], name, namelen) == 0 && client->scriptEnv[i][namelen] == '=') break; if (client->scriptEnv[i]) /* Reuse the slot. */ free(client->scriptEnv[i]); else { /* New variable. Expand if necessary. */ if (i >= client->scriptEnvsize - 1) { char **newscriptEnv; int newscriptEnvsize = client->scriptEnvsize + 50; newscriptEnv = realloc(client->scriptEnv, newscriptEnvsize); if (newscriptEnv == NULL) { free(client->scriptEnv); client->scriptEnv = NULL; client->scriptEnvsize = 0; error("script_set_env: no memory for variable"); } client->scriptEnv = newscriptEnv; client->scriptEnvsize = newscriptEnvsize; } /* need to set the NULL pointer at end of array beyond the new slot. */ client->scriptEnv[i + 1] = NULL; } /* Allocate space and format the variable in the appropriate slot. */ client->scriptEnv[i] = malloc(strlen(prefix) + strlen(name) + 1 + strlen(value) + 1); if (client->scriptEnv[i] == NULL) error("script_set_env: no memory for variable assignment"); /* No `` or $() command substitution allowed in environment values! */ for (j=0; j < strlen(value); j++) switch (value[j]) { case '`': case '$': error("illegal character (%c) in value '%s'", value[j], value); /* not reached */ } snprintf(client->scriptEnv[i], strlen(prefix) + strlen(name) + 1 + strlen(value) + 1, "%s%s=%s", prefix, name, value); } void script_flush_env(struct client_state *client) { int i; for (i = 0; client->scriptEnv[i]; i++) { free(client->scriptEnv[i]); client->scriptEnv[i] = NULL; } client->scriptEnvsize = 0; } int dhcp_option_ev_name(char *buf, size_t buflen, struct option *option) { int i; for (i = 0; option->name[i]; i++) { if (i + 1 == buflen) return 0; if (option->name[i] == '-') buf[i] = '_'; else buf[i] = option->name[i]; } buf[i] = 0; return 1; } void go_daemon(void) { static int state = 0; cap_rights_t rights; if (no_daemon || state) return; state = 1; /* Stop logging to stderr... */ log_perror = 0; if (daemon(1, 0) == -1) error("daemon"); cap_rights_init(&rights); if (pidfile != NULL) { pidfile_write(pidfile); if (cap_rights_limit(pidfile_fileno(pidfile), &rights) < 0 && errno != ENOSYS) { error("can't limit pidfile descriptor: %m"); } } /* we are chrooted, daemon(3) fails to open /dev/null */ if (nullfd != -1) { dup2(nullfd, STDIN_FILENO); dup2(nullfd, STDOUT_FILENO); dup2(nullfd, STDERR_FILENO); close(nullfd); nullfd = -1; } if (cap_rights_limit(STDIN_FILENO, &rights) < 0 && errno != ENOSYS) error("can't limit stdin: %m"); cap_rights_init(&rights, CAP_WRITE); if (cap_rights_limit(STDOUT_FILENO, &rights) < 0 && errno != ENOSYS) error("can't limit stdout: %m"); if (cap_rights_limit(STDERR_FILENO, &rights) < 0 && errno != ENOSYS) error("can't limit stderr: %m"); } int check_option(struct client_lease *l, int option) { char *opbuf; char *sbuf; /* we use this, since this is what gets passed to dhclient-script */ opbuf = pretty_print_option(option, l->options[option].data, l->options[option].len, 0, 0); sbuf = option_as_string(option, l->options[option].data, l->options[option].len); switch (option) { case DHO_SUBNET_MASK: case DHO_TIME_SERVERS: case DHO_NAME_SERVERS: case DHO_ROUTERS: case DHO_DOMAIN_NAME_SERVERS: case DHO_LOG_SERVERS: case DHO_COOKIE_SERVERS: case DHO_LPR_SERVERS: case DHO_IMPRESS_SERVERS: case DHO_RESOURCE_LOCATION_SERVERS: case DHO_SWAP_SERVER: case DHO_BROADCAST_ADDRESS: case DHO_NIS_SERVERS: case DHO_NTP_SERVERS: case DHO_NETBIOS_NAME_SERVERS: case DHO_NETBIOS_DD_SERVER: case DHO_FONT_SERVERS: case DHO_DHCP_SERVER_IDENTIFIER: case DHO_NISPLUS_SERVERS: case DHO_MOBILE_IP_HOME_AGENT: case DHO_SMTP_SERVER: case DHO_POP_SERVER: case DHO_NNTP_SERVER: case DHO_WWW_SERVER: case DHO_FINGER_SERVER: case DHO_IRC_SERVER: case DHO_STREETTALK_SERVER: case DHO_STREETTALK_DA_SERVER: if (!ipv4addrs(opbuf)) { warning("Invalid IP address in option: %s", opbuf); return (0); } return (1) ; case DHO_HOST_NAME: case DHO_NIS_DOMAIN: case DHO_NISPLUS_DOMAIN: case DHO_TFTP_SERVER_NAME: if (!res_hnok(sbuf)) { warning("Bogus Host Name option %d: %s (%s)", option, sbuf, opbuf); l->options[option].len = 0; free(l->options[option].data); } return (1); case DHO_DOMAIN_NAME: case DHO_DOMAIN_SEARCH: if (!res_hnok(sbuf)) { if (!check_search(sbuf)) { warning("Bogus domain search list %d: %s (%s)", option, sbuf, opbuf); l->options[option].len = 0; free(l->options[option].data); } } return (1); case DHO_PAD: case DHO_TIME_OFFSET: case DHO_BOOT_SIZE: case DHO_MERIT_DUMP: case DHO_ROOT_PATH: case DHO_EXTENSIONS_PATH: case DHO_IP_FORWARDING: case DHO_NON_LOCAL_SOURCE_ROUTING: case DHO_POLICY_FILTER: case DHO_MAX_DGRAM_REASSEMBLY: case DHO_DEFAULT_IP_TTL: case DHO_PATH_MTU_AGING_TIMEOUT: case DHO_PATH_MTU_PLATEAU_TABLE: case DHO_INTERFACE_MTU: case DHO_ALL_SUBNETS_LOCAL: case DHO_PERFORM_MASK_DISCOVERY: case DHO_MASK_SUPPLIER: case DHO_ROUTER_DISCOVERY: case DHO_ROUTER_SOLICITATION_ADDRESS: case DHO_STATIC_ROUTES: case DHO_TRAILER_ENCAPSULATION: case DHO_ARP_CACHE_TIMEOUT: case DHO_IEEE802_3_ENCAPSULATION: case DHO_DEFAULT_TCP_TTL: case DHO_TCP_KEEPALIVE_INTERVAL: case DHO_TCP_KEEPALIVE_GARBAGE: case DHO_VENDOR_ENCAPSULATED_OPTIONS: case DHO_NETBIOS_NODE_TYPE: case DHO_NETBIOS_SCOPE: case DHO_X_DISPLAY_MANAGER: case DHO_DHCP_REQUESTED_ADDRESS: case DHO_DHCP_LEASE_TIME: case DHO_DHCP_OPTION_OVERLOAD: case DHO_DHCP_MESSAGE_TYPE: case DHO_DHCP_PARAMETER_REQUEST_LIST: case DHO_DHCP_MESSAGE: case DHO_DHCP_MAX_MESSAGE_SIZE: case DHO_DHCP_RENEWAL_TIME: case DHO_DHCP_REBINDING_TIME: case DHO_DHCP_CLASS_IDENTIFIER: case DHO_DHCP_CLIENT_IDENTIFIER: case DHO_BOOTFILE_NAME: case DHO_DHCP_USER_CLASS_ID: case DHO_END: return (1); case DHO_CLASSLESS_ROUTES: return (check_classless_option(l->options[option].data, l->options[option].len)); default: warning("unknown dhcp option value 0x%x", option); return (unknown_ok); } } /* RFC 3442 The Classless Static Routes option checks */ int check_classless_option(unsigned char *data, int len) { int i = 0; unsigned char width; in_addr_t addr, mask; if (len < 5) { warning("Too small length: %d", len); return (0); } while(i < len) { width = data[i++]; if (width == 0) { i += 4; continue; } else if (width < 9) { addr = (in_addr_t)(data[i] << 24); i += 1; } else if (width < 17) { addr = (in_addr_t)(data[i] << 24) + (in_addr_t)(data[i + 1] << 16); i += 2; } else if (width < 25) { addr = (in_addr_t)(data[i] << 24) + (in_addr_t)(data[i + 1] << 16) + (in_addr_t)(data[i + 2] << 8); i += 3; } else if (width < 33) { addr = (in_addr_t)(data[i] << 24) + (in_addr_t)(data[i + 1] << 16) + (in_addr_t)(data[i + 2] << 8) + data[i + 3]; i += 4; } else { warning("Incorrect subnet width: %d", width); return (0); } mask = (in_addr_t)(~0) << (32 - width); addr = ntohl(addr); mask = ntohl(mask); /* * From RFC 3442: * ... After deriving a subnet number and subnet mask * from each destination descriptor, the DHCP client * MUST zero any bits in the subnet number where the * corresponding bit in the mask is zero... */ if ((addr & mask) != addr) { addr &= mask; data[i - 1] = (unsigned char)( (addr >> (((32 - width)/8)*8)) & 0xFF); } i += 4; } if (i > len) { warning("Incorrect data length: %d (must be %d)", len, i); return (0); } return (1); } int res_hnok(const char *dn) { int pch = PERIOD, ch = *dn++; while (ch != '\0') { int nch = *dn++; if (periodchar(ch)) { ; } else if (periodchar(pch)) { if (!borderchar(ch)) return (0); } else if (periodchar(nch) || nch == '\0') { if (!borderchar(ch)) return (0); } else { if (!middlechar(ch)) return (0); } pch = ch, ch = nch; } return (1); } int check_search(const char *srch) { int pch = PERIOD, ch = *srch++; int domains = 1; /* 256 char limit re resolv.conf(5) */ if (strlen(srch) > 256) return (0); while (whitechar(ch)) ch = *srch++; while (ch != '\0') { int nch = *srch++; if (periodchar(ch) || whitechar(ch)) { ; } else if (periodchar(pch)) { if (!borderchar(ch)) return (0); } else if (periodchar(nch) || nch == '\0') { if (!borderchar(ch)) return (0); } else { if (!middlechar(ch)) return (0); } if (!whitechar(ch)) { pch = ch; } else { while (whitechar(nch)) { nch = *srch++; } if (nch != '\0') domains++; pch = PERIOD; } ch = nch; } /* 6 domain limit re resolv.conf(5) */ if (domains > 6) return (0); return (1); } /* Does buf consist only of dotted decimal ipv4 addrs? * return how many if so, * otherwise, return 0 */ int ipv4addrs(char * buf) { struct in_addr jnk; int count = 0; while (inet_aton(buf, &jnk) == 1){ count++; while (periodchar(*buf) || digitchar(*buf)) buf++; if (*buf == '\0') return (count); while (*buf == ' ') buf++; } return (0); } char * option_as_string(unsigned int code, unsigned char *data, int len) { static char optbuf[32768]; /* XXX */ char *op = optbuf; int opleft = sizeof(optbuf); unsigned char *dp = data; if (code > 255) error("option_as_string: bad code %d", code); for (; dp < data + len; dp++) { if (!isascii(*dp) || !isprint(*dp)) { if (dp + 1 != data + len || *dp != 0) { snprintf(op, opleft, "\\%03o", *dp); op += 4; opleft -= 4; } } else if (*dp == '"' || *dp == '\'' || *dp == '$' || *dp == '`' || *dp == '\\') { *op++ = '\\'; *op++ = *dp; opleft -= 2; } else { *op++ = *dp; opleft--; } } if (opleft < 1) goto toobig; *op = 0; return optbuf; toobig: warning("dhcp option too large"); return ""; } int fork_privchld(int fd, int fd2) { struct pollfd pfd[1]; int nfds; switch (fork()) { case -1: error("cannot fork"); case 0: break; default: return (0); } setproctitle("%s [priv]", ifi->name); setsid(); dup2(nullfd, STDIN_FILENO); dup2(nullfd, STDOUT_FILENO); dup2(nullfd, STDERR_FILENO); close(nullfd); close(fd2); close(ifi->rfdesc); ifi->rfdesc = -1; for (;;) { pfd[0].fd = fd; pfd[0].events = POLLIN; if ((nfds = poll(pfd, 1, INFTIM)) == -1) if (errno != EINTR) error("poll error"); if (nfds == 0 || !(pfd[0].revents & POLLIN)) continue; dispatch_imsg(ifi, fd); } } Index: stable/10/sbin/hastd/subr.c =================================================================== --- stable/10/sbin/hastd/subr.c (revision 280249) +++ stable/10/sbin/hastd/subr.c (revision 280250) @@ -1,299 +1,299 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2011 Pawel Jakub Dawidek * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #ifdef HAVE_CAPSICUM -#include +#include #include #endif #include #include #include #include #include #include #include #include #include #include "hast.h" #include "subr.h" int vsnprlcat(char *str, size_t size, const char *fmt, va_list ap) { size_t len; len = strlen(str); return (vsnprintf(str + len, size - len, fmt, ap)); } int snprlcat(char *str, size_t size, const char *fmt, ...) { va_list ap; int result; va_start(ap, fmt); result = vsnprlcat(str, size, fmt, ap); va_end(ap); return (result); } int provinfo(struct hast_resource *res, bool dowrite) { struct stat sb; PJDLOG_ASSERT(res->hr_localpath != NULL && res->hr_localpath[0] != '\0'); if (res->hr_localfd == -1) { res->hr_localfd = open(res->hr_localpath, dowrite ? O_RDWR : O_RDONLY); if (res->hr_localfd == -1) { pjdlog_errno(LOG_ERR, "Unable to open %s", res->hr_localpath); return (-1); } } if (fstat(res->hr_localfd, &sb) == -1) { pjdlog_errno(LOG_ERR, "Unable to stat %s", res->hr_localpath); return (-1); } if (S_ISCHR(sb.st_mode)) { /* * If this is character device, it is most likely GEOM provider. */ if (ioctl(res->hr_localfd, DIOCGMEDIASIZE, &res->hr_local_mediasize) == -1) { pjdlog_errno(LOG_ERR, "Unable obtain provider %s mediasize", res->hr_localpath); return (-1); } if (ioctl(res->hr_localfd, DIOCGSECTORSIZE, &res->hr_local_sectorsize) == -1) { pjdlog_errno(LOG_ERR, "Unable obtain provider %s sectorsize", res->hr_localpath); return (-1); } } else if (S_ISREG(sb.st_mode)) { /* * We also support regular files for which we hardcode * sector size of 512 bytes. */ res->hr_local_mediasize = sb.st_size; res->hr_local_sectorsize = 512; } else { /* * We support no other file types. */ pjdlog_error("%s is neither GEOM provider nor regular file.", res->hr_localpath); errno = EFTYPE; return (-1); } return (0); } const char * role2str(int role) { switch (role) { case HAST_ROLE_INIT: return ("init"); case HAST_ROLE_PRIMARY: return ("primary"); case HAST_ROLE_SECONDARY: return ("secondary"); } return ("unknown"); } int drop_privs(const struct hast_resource *res) { char jailhost[sizeof(res->hr_name) * 2]; struct jail jailst; struct passwd *pw; uid_t ruid, euid, suid; gid_t rgid, egid, sgid; gid_t gidset[1]; bool capsicum, jailed; /* * According to getpwnam(3) we have to clear errno before calling the * function to be able to distinguish between an error and missing * entry (with is not treated as error by getpwnam(3)). */ errno = 0; pw = getpwnam(HAST_USER); if (pw == NULL) { if (errno != 0) { pjdlog_errno(LOG_ERR, "Unable to find info about '%s' user", HAST_USER); return (-1); } else { pjdlog_error("'%s' user doesn't exist.", HAST_USER); errno = ENOENT; return (-1); } } bzero(&jailst, sizeof(jailst)); jailst.version = JAIL_API_VERSION; jailst.path = pw->pw_dir; if (res == NULL) { (void)snprintf(jailhost, sizeof(jailhost), "hastctl"); } else { (void)snprintf(jailhost, sizeof(jailhost), "hastd: %s (%s)", res->hr_name, role2str(res->hr_role)); } jailst.hostname = jailhost; jailst.jailname = NULL; jailst.ip4s = 0; jailst.ip4 = NULL; jailst.ip6s = 0; jailst.ip6 = NULL; if (jail(&jailst) >= 0) { jailed = true; } else { jailed = false; pjdlog_errno(LOG_WARNING, "Unable to jail to directory to %s", pw->pw_dir); if (chroot(pw->pw_dir) == -1) { pjdlog_errno(LOG_ERR, "Unable to change root directory to %s", pw->pw_dir); return (-1); } } PJDLOG_VERIFY(chdir("/") == 0); gidset[0] = pw->pw_gid; if (setgroups(1, gidset) == -1) { pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u", (unsigned int)pw->pw_gid); return (-1); } if (setgid(pw->pw_gid) == -1) { pjdlog_errno(LOG_ERR, "Unable to set gid to %u", (unsigned int)pw->pw_gid); return (-1); } if (setuid(pw->pw_uid) == -1) { pjdlog_errno(LOG_ERR, "Unable to set uid to %u", (unsigned int)pw->pw_uid); return (-1); } #ifdef HAVE_CAPSICUM capsicum = (cap_enter() == 0); if (!capsicum) { pjdlog_common(LOG_DEBUG, 1, errno, "Unable to sandbox using capsicum"); } else if (res != NULL) { cap_rights_t rights; static const unsigned long geomcmds[] = { DIOCGDELETE, DIOCGFLUSH }; PJDLOG_ASSERT(res->hr_role == HAST_ROLE_PRIMARY || res->hr_role == HAST_ROLE_SECONDARY); cap_rights_init(&rights, CAP_FLOCK, CAP_IOCTL, CAP_PREAD, CAP_PWRITE); if (cap_rights_limit(res->hr_localfd, &rights) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit capability rights on local descriptor"); } if (cap_ioctls_limit(res->hr_localfd, geomcmds, sizeof(geomcmds) / sizeof(geomcmds[0])) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit allowed GEOM ioctls"); } if (res->hr_role == HAST_ROLE_PRIMARY) { static const unsigned long ggatecmds[] = { G_GATE_CMD_MODIFY, G_GATE_CMD_START, G_GATE_CMD_DONE, G_GATE_CMD_DESTROY }; cap_rights_init(&rights, CAP_IOCTL); if (cap_rights_limit(res->hr_ggatefd, &rights) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit capability rights to CAP_IOCTL on ggate descriptor"); } if (cap_ioctls_limit(res->hr_ggatefd, ggatecmds, sizeof(ggatecmds) / sizeof(ggatecmds[0])) == -1) { pjdlog_errno(LOG_ERR, "Unable to limit allowed ggate ioctls"); } } } #else capsicum = false; #endif /* * Better be sure that everything succeeded. */ PJDLOG_VERIFY(getresuid(&ruid, &euid, &suid) == 0); PJDLOG_VERIFY(ruid == pw->pw_uid); PJDLOG_VERIFY(euid == pw->pw_uid); PJDLOG_VERIFY(suid == pw->pw_uid); PJDLOG_VERIFY(getresgid(&rgid, &egid, &sgid) == 0); PJDLOG_VERIFY(rgid == pw->pw_gid); PJDLOG_VERIFY(egid == pw->pw_gid); PJDLOG_VERIFY(sgid == pw->pw_gid); PJDLOG_VERIFY(getgroups(0, NULL) == 1); PJDLOG_VERIFY(getgroups(1, gidset) == 1); PJDLOG_VERIFY(gidset[0] == pw->pw_gid); pjdlog_debug(1, "Privileges successfully dropped using %s%s+setgid+setuid.", capsicum ? "capsicum+" : "", jailed ? "jail" : "chroot"); return (0); } Index: stable/10/tools/regression/capsicum/syscalls/cap_fcntls_limit.c =================================================================== --- stable/10/tools/regression/capsicum/syscalls/cap_fcntls_limit.c (revision 280249) +++ stable/10/tools/regression/capsicum/syscalls/cap_fcntls_limit.c (revision 280250) @@ -1,540 +1,540 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include #include "misc.h" static void fcntl_tests_0(int fd) { uint32_t fcntlrights; fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(cap_fcntls_limit(fd, ~CAP_FCNTL_ALL) == -1); CHECK(errno == EINVAL); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(cap_fcntls_limit(fd, 0) == 0); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); } static void fcntl_tests_1(int fd) { uint32_t fcntlrights; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_FCNTL) == 0); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); } static void fcntl_tests_2(int fd) { uint32_t fcntlrights; CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_FCNTL) == 0); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = CAP_FCNTL_ALL; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); } static void fcntl_tests_send_0(int sock) { int fd; CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_fcntls_limit(fd, 0) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); } static void fcntl_tests_recv_0(int sock) { uint32_t fcntlrights; int fd; CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == (CAP_FCNTL_GETFL | CAP_FCNTL_SETFL)); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == 0); CHECK(fcntl(fd, F_GETFL) == (O_RDWR | O_NONBLOCK)); CHECK(fcntl(fd, F_SETFL, 0) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == CAP_FCNTL_GETFL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFL) == O_RDWR); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL | CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_GETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); errno = 0; CHECK(cap_fcntls_limit(fd, CAP_FCNTL_SETFL) == -1); CHECK(errno == ENOTCAPABLE); fcntlrights = 0; CHECK(cap_fcntls_get(fd, &fcntlrights) == 0); CHECK(fcntlrights == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, O_NONBLOCK) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_SETFL, 0) == -1); CHECK(errno == ENOTCAPABLE); errno = 0; CHECK(fcntl(fd, F_GETFL) == -1); CHECK(errno == ENOTCAPABLE); CHECK(close(fd) == 0); } int main(void) { int fd, pfd, sp[2]; pid_t pid; printf("1..870\n"); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); fcntl_tests_0(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); fcntl_tests_1(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); fcntl_tests_2(fd); CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { fcntl_tests_0(fd); CHECK(close(fd) == 0); exit(0); } else { CHECK(waitpid(pid, NULL, 0) == pid); fcntl_tests_0(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { sleep(1); fcntl_tests_0(fd); CHECK(close(fd) == 0); exit(0); } else { fcntl_tests_0(fd); CHECK(waitpid(pid, NULL, 0) == pid); } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = pdfork(&pfd, 0)) >= 0); if (pid == 0) { fcntl_tests_1(fd); exit(0); } else { CHECK(pdwait(pfd) == 0); /* It fails with EBADF, which I believe is a bug. CHECK(close(pfd) == 0); */ fcntl_tests_1(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = pdfork(&pfd, 0)) >= 0); if (pid == 0) { sleep(1); fcntl_tests_1(fd); exit(0); } else { fcntl_tests_1(fd); CHECK(pdwait(pfd) == 0); /* It fails with EBADF, which I believe is a bug. CHECK(close(pfd) == 0); */ } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { fcntl_tests_2(fd); exit(0); } else { CHECK(waitpid(pid, NULL, 0) == pid); fcntl_tests_2(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK((pid = fork()) >= 0); if (pid == 0) { sleep(1); fcntl_tests_2(fd); exit(0); } else { fcntl_tests_2(fd); CHECK(waitpid(pid, NULL, 0) == pid); } CHECK(close(fd) == 0); /* Send descriptors from parent to child. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); fcntl_tests_recv_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); fcntl_tests_send_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } /* Send descriptors from child to parent. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); fcntl_tests_send_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); fcntl_tests_recv_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } exit(0); } Index: stable/10/tools/regression/capsicum/syscalls/cap_getmode.c =================================================================== --- stable/10/tools/regression/capsicum/syscalls/cap_getmode.c (revision 280249) +++ stable/10/tools/regression/capsicum/syscalls/cap_getmode.c (revision 280250) @@ -1,167 +1,167 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include "misc.h" int main(void) { unsigned int mode; pid_t pid; int pfd; printf("1..27\n"); mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are not in capability mode. */ CHECK(mode == 0); /* Expect EFAULT. */ errno = 0; CHECK(cap_getmode(NULL) == -1); CHECK(errno == EFAULT); errno = 0; CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1); CHECK(errno == EFAULT); /* If parent is not in capability mode, child after fork() also won't be. */ pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are not in capability mode. */ CHECK(mode == 0); exit(0); default: if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); } /* If parent is not in capability mode, child after pdfork() also won't be. */ pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are not in capability mode. */ CHECK(mode == 0); exit(0); default: if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); } /* In capability mode... */ CHECK(cap_enter() == 0); mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are in capability mode. */ CHECK(mode == 1); /* Expect EFAULT. */ errno = 0; CHECK(cap_getmode(NULL) == -1); CHECK(errno == EFAULT); errno = 0; CHECK(cap_getmode((void *)(uintptr_t)0xdeadc0de) == -1); CHECK(errno == EFAULT); /* If parent is in capability mode, child after fork() also will be. */ pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are in capability mode. */ CHECK(mode == 1); exit(0); default: /* * wait(2) and friends are not permitted in the capability mode, * so we can only just wait for a while. */ sleep(1); } /* If parent is in capability mode, child after pdfork() also will be. */ pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: mode = 666; CHECK(cap_getmode(&mode) == 0); /* If cap_getmode() succeeded mode should be modified. */ CHECK(mode != 666); /* We are in capability mode. */ CHECK(mode == 1); exit(0); default: if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); } exit(0); } Index: stable/10/tools/regression/capsicum/syscalls/cap_ioctls_limit.c =================================================================== --- stable/10/tools/regression/capsicum/syscalls/cap_ioctls_limit.c (revision 280249) +++ stable/10/tools/regression/capsicum/syscalls/cap_ioctls_limit.c (revision 280250) @@ -1,462 +1,462 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Pawel Jakub Dawidek under sponsorship from * the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include "misc.h" static void ioctl_tests_0(int fd) { unsigned long cmds[2]; CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds)); CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) || (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX)); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, 1) == nitems(cmds)); CHECK(cmds[0] == FIOCLEX || cmds[0] == FIONCLEX); CHECK(cmds[1] == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); cmds[0] = FIOCLEX; CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); CHECK(errno == ENOTCAPABLE); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(cap_ioctls_limit(fd, NULL, 0) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); } static void ioctl_tests_1(int fd) { unsigned long cmds[2]; cmds[0] = FIOCLEX; CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); CHECK(cmds[1] == 0); CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_IOCTL) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); } static void ioctl_tests_2(int fd) { unsigned long cmds[2]; CHECK(cap_rights_limit(fd, CAP_ALL & ~CAP_IOCTL) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); cmds[0] = FIOCLEX; errno = 0; CHECK(cap_ioctls_limit(fd, cmds, 1) == -1); CHECK(errno == ENOTCAPABLE); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); } static void ioctl_tests_send_0(int sock) { unsigned long cmds[2]; int fd; CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); cmds[0] = FIOCLEX; cmds[1] = FIONCLEX; CHECK(cap_ioctls_limit(fd, cmds, nitems(cmds)) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); cmds[0] = FIOCLEX; CHECK(cap_ioctls_limit(fd, cmds, 1) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); CHECK(cap_ioctls_limit(fd, NULL, 0) == 0); CHECK(descriptor_send(sock, fd) == 0); CHECK(close(fd) == 0); } static void ioctl_tests_recv_0(int sock) { unsigned long cmds[2]; int fd; CHECK(descriptor_recv(sock, &fd) == 0); CHECK(cap_ioctls_get(fd, NULL, 0) == CAP_IOCTLS_ALL); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == nitems(cmds)); CHECK((cmds[0] == FIOCLEX && cmds[1] == FIONCLEX) || (cmds[0] == FIONCLEX && cmds[1] == FIOCLEX)); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(ioctl(fd, FIONCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); cmds[0] = cmds[1] = 0; CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 1); CHECK(cmds[0] == FIOCLEX); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(ioctl(fd, FIOCLEX) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); CHECK(descriptor_recv(sock, &fd) == 0); CHECK(cap_ioctls_get(fd, cmds, nitems(cmds)) == 0); CHECK(fcntl(fd, F_GETFD) == 0); errno = 0; CHECK(ioctl(fd, FIOCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(fcntl(fd, F_SETFD, FD_CLOEXEC) == 0); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); errno = 0; CHECK(ioctl(fd, FIONCLEX) == -1); CHECK(errno == ENOTCAPABLE); CHECK(fcntl(fd, F_GETFD) == FD_CLOEXEC); CHECK(fcntl(fd, F_SETFD, 0) == 0); CHECK(fcntl(fd, F_GETFD) == 0); CHECK(close(fd) == 0); } int main(void) { int fd, pfd, sp[2]; pid_t pid; printf("1..607\n"); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); ioctl_tests_0(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); ioctl_tests_1(fd); CHECK(close(fd) == 0); CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); ioctl_tests_2(fd); CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: ioctl_tests_0(fd); CHECK(close(fd) == 0); exit(0); default: if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); ioctl_tests_0(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: sleep(1); ioctl_tests_0(fd); CHECK(close(fd) == 0); exit(0); default: ioctl_tests_0(fd); if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: ioctl_tests_1(fd); exit(0); default: if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); ioctl_tests_1(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = pdfork(&pfd, 0); switch (pid) { case -1: err(1, "pdfork() failed"); case 0: sleep(1); ioctl_tests_1(fd); exit(0); default: ioctl_tests_1(fd); if (pdwait(pfd) == -1) err(1, "pdwait() failed"); close(pfd); } CHECK(close(fd) == 0); /* Child inherits descriptor and operates on it first. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: ioctl_tests_2(fd); exit(0); default: if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); ioctl_tests_2(fd); } CHECK(close(fd) == 0); /* Child inherits descriptor, but operates on it after parent. */ CHECK((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0); pid = fork(); switch (pid) { case -1: err(1, "fork() failed"); case 0: sleep(1); ioctl_tests_2(fd); exit(0); default: ioctl_tests_2(fd); if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid() failed"); } CHECK(close(fd) == 0); /* Send descriptors from parent to child. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); ioctl_tests_recv_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); ioctl_tests_send_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } /* Send descriptors from child to parent. */ CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp) == 0); CHECK((pid = fork()) >= 0); if (pid == 0) { CHECK(close(sp[0]) == 0); ioctl_tests_send_0(sp[1]); CHECK(close(sp[1]) == 0); exit(0); } else { CHECK(close(sp[1]) == 0); ioctl_tests_recv_0(sp[0]); CHECK(waitpid(pid, NULL, 0) == pid); CHECK(close(sp[0]) == 0); } exit(0); } Index: stable/10/tools/regression/security/cap_test/cap_test_capabilities.c =================================================================== --- stable/10/tools/regression/security/cap_test/cap_test_capabilities.c (revision 280249) +++ stable/10/tools/regression/security/cap_test/cap_test_capabilities.c (revision 280250) @@ -1,561 +1,561 @@ /*- * Copyright (c) 2009-2011 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson * Copyright (c) 2012 FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Pawel Jakub Dawidek under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Test whether various operations on capabilities are properly masked for * various object types. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "cap_test.h" #define SYSCALL_FAIL(syscall, message) \ FAIL("%s:\t%s (rights 0x%jx)", #syscall, message, rights) /* * Ensure that, if the capability had enough rights for the system call to * pass, then it did. Otherwise, ensure that the errno is ENOTCAPABLE; * capability restrictions should kick in before any other error logic. */ #define CHECK_RESULT(syscall, rights_needed, succeeded) do { \ if ((rights & (rights_needed)) == (rights_needed)) { \ if (succeeded) { \ if (success == -1) \ success = PASSED; \ } else { \ SYSCALL_FAIL(syscall, "failed"); \ } \ } else { \ if (succeeded) { \ FAILX("%s:\tsucceeded when it shouldn't have" \ " (rights 0x%jx)", #syscall, \ (uintmax_t)rights); \ } else if (errno != ENOTCAPABLE) { \ SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ } \ } \ errno = 0; \ } while (0) /* * As above, but for the special mmap() case: unmap after successful mmap(). */ #define CHECK_MMAP_RESULT(rights_needed) do { \ if ((rights & (rights_needed)) == (rights_needed)) { \ if (p == MAP_FAILED) \ SYSCALL_FAIL(mmap, "failed"); \ else { \ (void)munmap(p, getpagesize()); \ if (success == -1) \ success = PASSED; \ } \ } else { \ if (p != MAP_FAILED) { \ FAILX("%s:\tsucceeded when it shouldn't have" \ " (rights 0x%jx)", "mmap", rights); \ (void)munmap(p, getpagesize()); \ } else if (errno != ENOTCAPABLE) \ SYSCALL_FAIL(syscall, "errno != ENOTCAPABLE"); \ } \ errno = 0; \ } while (0) /* * Given a file descriptor, create a capability with specific rights and * make sure only those rights work. */ static int try_file_ops(int filefd, int dirfd, cap_rights_t rights) { struct stat sb; struct statfs sf; cap_rights_t erights; int fd_cap, fd_capcap, dfd_cap; ssize_t ssize, ssize2; off_t off; void *p; char ch; int ret, is_nfs; struct pollfd pollfd; int success = -1; REQUIRE(fstatfs(filefd, &sf)); is_nfs = (strcmp("nfs", sf.f_fstypename) == 0); REQUIRE(fd_cap = cap_new(filefd, rights)); CHECK(cap_getrights(fd_cap, &erights) == 0); CHECK(rights == erights); REQUIRE(fd_capcap = cap_new(fd_cap, rights)); CHECK(cap_getrights(fd_capcap, &erights) == 0); CHECK(rights == erights); CHECK(fd_capcap != fd_cap); REQUIRE(dfd_cap = cap_new(dirfd, rights)); CHECK(cap_getrights(dfd_cap, &erights) == 0); CHECK(rights == erights); ssize = read(fd_cap, &ch, sizeof(ch)); CHECK_RESULT(read, CAP_READ, ssize >= 0); ssize = write(fd_cap, &ch, sizeof(ch)); CHECK_RESULT(write, CAP_WRITE, ssize >= 0); off = lseek(fd_cap, 0, SEEK_SET); CHECK_RESULT(lseek, CAP_SEEK, off >= 0); ssize = pread(fd_cap, &ch, sizeof(ch), 0); ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); CHECK_RESULT(pread, CAP_PREAD, ssize >= 0); CHECK(ssize == ssize2); ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); CHECK_RESULT(pwrite, CAP_PWRITE, ssize >= 0); p = mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP); p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_R); p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_W); p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_X); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_RW); p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_RX); p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_WX); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_RWX); ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600); CHECK_RESULT(openat(O_CREATE | O_RDONLY), CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY | O_APPEND, 0600); CHECK_RESULT(openat(O_CREATE | O_WRONLY | O_APPEND), CAP_CREATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR | O_APPEND, 0600); CHECK_RESULT(openat(O_CREATE | O_RDWR | O_APPEND), CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = fsync(fd_cap); CHECK_RESULT(fsync, CAP_FSYNC, ret == 0); ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDONLY); CHECK_RESULT(openat(O_FSYNC | O_RDONLY), CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY | O_APPEND); CHECK_RESULT(openat(O_FSYNC | O_WRONLY | O_APPEND), CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR | O_APPEND); CHECK_RESULT(openat(O_FSYNC | O_RDWR | O_APPEND), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDONLY); CHECK_RESULT(openat(O_SYNC | O_RDONLY), CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY | O_APPEND); CHECK_RESULT(openat(O_SYNC | O_WRONLY | O_APPEND), CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR | O_APPEND); CHECK_RESULT(openat(O_SYNC | O_RDWR | O_APPEND), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); ret = ftruncate(fd_cap, 0); CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); ret = openat(dirfd, "cap_ftruncate", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDONLY); CHECK_RESULT(openat(O_TRUNC | O_RDONLY), CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_WRONLY); CHECK_RESULT(openat(O_TRUNC | O_WRONLY), CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDWR); CHECK_RESULT(openat(O_TRUNC | O_RDWR), CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(unlinkat(dirfd, "cap_ftruncate", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600); CHECK_RESULT(openat(O_CREATE | O_WRONLY), CAP_CREATE | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600); CHECK_RESULT(openat(O_CREATE | O_RDWR), CAP_CREATE | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY); CHECK_RESULT(openat(O_FSYNC | O_WRONLY), CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR); CHECK_RESULT(openat(O_FSYNC | O_RDWR), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY); CHECK_RESULT(openat(O_SYNC | O_WRONLY), CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR); CHECK_RESULT(openat(O_SYNC | O_RDWR), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); /* * Note: this is not expected to work over NFS. */ ret = fchflags(fd_cap, UF_NODUMP); CHECK_RESULT(fchflags, CAP_FCHFLAGS, ret == 0 || (is_nfs && errno == EOPNOTSUPP)); ret = openat(dirfd, "cap_chflagsat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0); CHECK_RESULT(chflagsat, CAP_CHFLAGSAT | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_chflagsat", 0) == 0); ret = fchown(fd_cap, -1, -1); CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0); CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0); ret = fchmod(fd_cap, 0644); CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0); CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0); ret = fcntl(fd_cap, F_GETFL); CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0); ret = fcntl(fd_cap, F_SETFL, ret); CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0); /* XXX flock */ ret = fstat(fd_cap, &sb); CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0); CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0); ret = fstatfs(fd_cap, &sf); CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); ret = fpathconf(fd_cap, _PC_NAME_MAX); CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0); ret = futimes(fd_cap, NULL); CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = futimesat(dfd_cap, "cap_futimesat", NULL); CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0); ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0); CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0); ret = mkdirat(dfd_cap, "cap_mkdirat", 0700); CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0); ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600); CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0); ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0); CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0); /* TODO: renameat(2) */ ret = symlinkat("test", dfd_cap, "cap_symlinkat"); CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0); ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = unlinkat(dfd_cap, "cap_unlinkat", 0); CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0); ret = mkdirat(dirfd, "cap_unlinkat", 0700); CHECK(ret == 0); ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR); CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0); pollfd.fd = fd_cap; pollfd.events = POLLIN | POLLERR | POLLHUP; pollfd.revents = 0; ret = poll(&pollfd, 1, 0); if (rights & CAP_EVENT) CHECK((pollfd.revents & POLLNVAL) == 0); else CHECK((pollfd.revents & POLLNVAL) != 0); /* XXX: select, kqueue */ close(fd_cap); close(fd_capcap); if (success == -1) { fprintf(stderr, "No tests for rights 0x%jx.\n", (uintmax_t)rights); success = FAILED; } return (success); } #define TRY(rights) \ do { \ if (success == PASSED) \ success = try_file_ops(filefd, dirfd, (rights)); \ else \ /* We've already failed, but try the test anyway. */ \ try_file_ops(filefd, dirfd, (rights)); \ } while (0) #define KEEP_ERRNO(...) do { \ int _saved_errno = errno; \ __VA_ARGS__; \ errno = _saved_errno; \ } while (0); int test_capabilities(void) { int filefd, dirfd, tmpfd; int success = PASSED; char file[] = "/tmp/cap_test.XXXXXXXXXX"; char dir[] = "/tmp/cap_test.XXXXXXXXXX"; filefd = mkstemp(file); if (filefd < 0) err(-1, "mkstemp"); if (mkdtemp(dir) == NULL) { KEEP_ERRNO(unlink(file)); err(-1, "mkdtemp"); } dirfd = open(dir, O_RDONLY | O_DIRECTORY); if (dirfd == -1) { KEEP_ERRNO(unlink(file)); KEEP_ERRNO(rmdir(dir)); err(-1, "open"); } tmpfd = open("/tmp", O_RDONLY | O_DIRECTORY); if (tmpfd == -1) { KEEP_ERRNO(unlink(file)); KEEP_ERRNO(rmdir(dir)); err(-1, "open"); } if (cap_enter() == -1) { KEEP_ERRNO(unlink(file)); KEEP_ERRNO(rmdir(dir)); err(-1, "cap_enter"); } TRY(CAP_READ); TRY(CAP_WRITE); TRY(CAP_SEEK); TRY(CAP_PREAD); TRY(CAP_PWRITE); TRY(CAP_READ | CAP_WRITE); TRY(CAP_PREAD | CAP_PWRITE); TRY(CAP_MMAP); TRY(CAP_MMAP_R); TRY(CAP_MMAP_W); TRY(CAP_MMAP_X); TRY(CAP_MMAP_RW); TRY(CAP_MMAP_RX); TRY(CAP_MMAP_WX); TRY(CAP_MMAP_RWX); TRY(CAP_CREATE | CAP_READ | CAP_LOOKUP); TRY(CAP_CREATE | CAP_WRITE | CAP_LOOKUP); TRY(CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); #ifdef TODO TRY(CAP_FEXECVE); #endif TRY(CAP_FSYNC); TRY(CAP_FSYNC | CAP_READ | CAP_LOOKUP); TRY(CAP_FSYNC | CAP_WRITE | CAP_LOOKUP); TRY(CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP); TRY(CAP_FTRUNCATE); TRY(CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP); TRY(CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP); TRY(CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP); #ifdef TODO TRY(CAP_FCHDIR); #endif TRY(CAP_FCHFLAGS); TRY(CAP_FCHOWN); TRY(CAP_FCHOWN | CAP_LOOKUP); TRY(CAP_FCHMOD | CAP_LOOKUP); TRY(CAP_FCNTL); #ifdef TODO TRY(CAP_FLOCK); #endif TRY(CAP_FPATHCONF); #ifdef TODO TRY(CAP_FSCK); #endif TRY(CAP_FSTAT | CAP_LOOKUP); TRY(CAP_FSTATFS); TRY(CAP_FUTIMES | CAP_LOOKUP); TRY(CAP_LINKAT | CAP_LOOKUP); TRY(CAP_MKDIRAT | CAP_LOOKUP); TRY(CAP_MKFIFOAT | CAP_LOOKUP); TRY(CAP_MKNODAT | CAP_LOOKUP); TRY(CAP_SYMLINKAT | CAP_LOOKUP); TRY(CAP_UNLINKAT | CAP_LOOKUP); /* Rename needs CAP_RENAMEAT on source directory and CAP_LINKAT on destination directory. */ TRY(CAP_RENAMEAT | CAP_UNLINKAT | CAP_LOOKUP); #ifdef TODO TRY(CAP_LOOKUP); TRY(CAP_EXTATTR_DELETE); TRY(CAP_EXTATTR_GET); TRY(CAP_EXTATTR_LIST); TRY(CAP_EXTATTR_SET); TRY(CAP_ACL_CHECK); TRY(CAP_ACL_DELETE); TRY(CAP_ACL_GET); TRY(CAP_ACL_SET); TRY(CAP_ACCEPT); TRY(CAP_BIND); TRY(CAP_CONNECT); TRY(CAP_GETPEERNAME); TRY(CAP_GETSOCKNAME); TRY(CAP_GETSOCKOPT); TRY(CAP_LISTEN); TRY(CAP_PEELOFF); TRY(CAP_RECV); TRY(CAP_SEND); TRY(CAP_SETSOCKOPT); TRY(CAP_SHUTDOWN); TRY(CAP_MAC_GET); TRY(CAP_MAC_SET); TRY(CAP_SEM_GETVALUE); TRY(CAP_SEM_POST); TRY(CAP_SEM_WAIT); TRY(CAP_POST_EVENT); TRY(CAP_EVENT); TRY(CAP_IOCTL); TRY(CAP_TTYHOOK); TRY(CAP_PDGETPID); TRY(CAP_PDWAIT); TRY(CAP_PDKILL); #endif (void)unlinkat(tmpfd, file + strlen("/tmp/"), 0); (void)unlinkat(tmpfd, dir + strlen("/tmp/"), AT_REMOVEDIR); return (success); } Index: stable/10/tools/regression/security/cap_test/cap_test_capmode.c =================================================================== --- stable/10/tools/regression/security/cap_test/cap_test_capmode.c (revision 280249) +++ stable/10/tools/regression/security/cap_test/cap_test_capmode.c (revision 280250) @@ -1,200 +1,200 @@ /*- * Copyright (c) 2008-2009 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Test routines to make sure a variety of system calls are or are not * available in capability mode. The goal is not to see if they work, just * whether or not they return the expected ECAPMODE. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "cap_test.h" #define CHECK_SYSCALL_VOID_NOT_ECAPMODE(syscall, ...) do { \ errno = 0; \ (void)syscall(__VA_ARGS__); \ if (errno == ECAPMODE) \ FAIL("capmode: %s failed with ECAPMODE", #syscall); \ } while (0) int test_capmode(void) { struct statfs statfs; struct stat sb; long sysarch_arg = 0; int fd_close, fd_dir, fd_file, fd_socket, fd2[2]; int success = PASSED; pid_t pid, wpid; char ch; /* Open some files to play with. */ REQUIRE(fd_file = open("/tmp/cap_capmode", O_RDWR|O_CREAT, 0644)); REQUIRE(fd_close = open("/dev/null", O_RDWR)); REQUIRE(fd_dir = open("/tmp", O_RDONLY)); REQUIRE(fd_socket = socket(PF_INET, SOCK_DGRAM, 0)); /* Enter capability mode. */ REQUIRE(cap_enter()); /* * System calls that are not permitted in capability mode. */ CHECK_CAPMODE(access, "/tmp/cap_capmode_access", F_OK); CHECK_CAPMODE(acct, "/tmp/cap_capmode_acct"); CHECK_CAPMODE(bind, PF_INET, NULL, 0); CHECK_CAPMODE(chdir, "/tmp/cap_capmode_chdir"); CHECK_CAPMODE(chflags, "/tmp/cap_capmode_chflags", UF_NODUMP); CHECK_CAPMODE(chmod, "/tmp/cap_capmode_chmod", 0644); CHECK_CAPMODE(chown, "/tmp/cap_capmode_chown", -1, -1); CHECK_CAPMODE(chroot, "/tmp/cap_capmode_chroot"); CHECK_CAPMODE(connect, PF_INET, NULL, 0); CHECK_CAPMODE(creat, "/tmp/cap_capmode_creat", 0644); CHECK_CAPMODE(fchdir, fd_dir); CHECK_CAPMODE(getfsstat, &statfs, sizeof(statfs), MNT_NOWAIT); CHECK_CAPMODE(link, "/tmp/foo", "/tmp/bar"); CHECK_CAPMODE(lstat, "/tmp/cap_capmode_lstat", &sb); CHECK_CAPMODE(mknod, "/tmp/capmode_mknod", 06440, 0); CHECK_CAPMODE(mount, "procfs", "/not_mounted", 0, NULL); CHECK_CAPMODE(open, "/dev/null", O_RDWR); CHECK_CAPMODE(readlink, "/tmp/cap_capmode_readlink", NULL, 0); CHECK_CAPMODE(revoke, "/tmp/cap_capmode_revoke"); CHECK_CAPMODE(stat, "/tmp/cap_capmode_stat", &sb); CHECK_CAPMODE(symlink, "/tmp/cap_capmode_symlink_from", "/tmp/cap_capmode_symlink_to"); CHECK_CAPMODE(unlink, "/tmp/cap_capmode_unlink"); CHECK_CAPMODE(unmount, "/not_mounted", 0); /* * System calls that are permitted in capability mode. */ CHECK_SYSCALL_SUCCEEDS(close, fd_close); CHECK_SYSCALL_SUCCEEDS(dup, fd_file); CHECK_SYSCALL_SUCCEEDS(fstat, fd_file, &sb); CHECK_SYSCALL_SUCCEEDS(lseek, fd_file, 0, SEEK_SET); CHECK_SYSCALL_SUCCEEDS(msync, &fd_file, 8192, MS_ASYNC); CHECK_SYSCALL_SUCCEEDS(profil, NULL, 0, 0, 0); CHECK_SYSCALL_SUCCEEDS(read, fd_file, &ch, sizeof(ch)); CHECK_SYSCALL_SUCCEEDS(recvfrom, fd_socket, NULL, 0, 0, NULL, NULL); CHECK_SYSCALL_SUCCEEDS(setuid, getuid()); CHECK_SYSCALL_SUCCEEDS(write, fd_file, &ch, sizeof(ch)); /* * These calls will fail for lack of e.g. a proper name to send to, * but they are allowed in capability mode, so errno != ECAPMODE. */ CHECK_NOT_CAPMODE(accept, fd_socket, NULL, NULL); CHECK_NOT_CAPMODE(getpeername, fd_socket, NULL, NULL); CHECK_NOT_CAPMODE(getsockname, fd_socket, NULL, NULL); CHECK_NOT_CAPMODE(fchflags, fd_file, UF_NODUMP); CHECK_NOT_CAPMODE(recvmsg, fd_socket, NULL, 0); CHECK_NOT_CAPMODE(sendmsg, fd_socket, NULL, 0); CHECK_NOT_CAPMODE(sendto, fd_socket, NULL, 0, 0, NULL, 0); /* * System calls which should be allowed in capability mode, but which * don't return errors, and are thus difficult to check. * * We will try anyway, by checking errno. */ CHECK_SYSCALL_VOID_NOT_ECAPMODE(getegid); CHECK_SYSCALL_VOID_NOT_ECAPMODE(geteuid); CHECK_SYSCALL_VOID_NOT_ECAPMODE(getgid); CHECK_SYSCALL_VOID_NOT_ECAPMODE(getpid); CHECK_SYSCALL_VOID_NOT_ECAPMODE(getppid); CHECK_SYSCALL_VOID_NOT_ECAPMODE(getuid); /* * Finally, tests for system calls that don't fit the pattern very well. */ pid = fork(); if (pid >= 0) { if (pid == 0) { exit(0); } else if (pid > 0) { wpid = waitpid(pid, NULL, 0); if (wpid < 0) { if (errno != ECAPMODE) FAIL("capmode:waitpid"); } else FAIL("capmode:waitpid succeeded"); } } else FAIL("capmode:fork"); if (getlogin() == NULL) FAIL("test_sycalls:getlogin %d", errno); if (getsockname(fd_socket, NULL, NULL) < 0) { if (errno == ECAPMODE) FAIL("capmode:getsockname"); } /* XXXRW: ktrace */ if (pipe(fd2) == 0) { close(fd2[0]); close(fd2[1]); } else if (errno == ECAPMODE) FAIL("capmode:pipe"); /* XXXRW: ptrace. */ /* sysarch() is, by definition, architecture-dependent */ #if defined (__amd64__) || defined (__i386__) CHECK_CAPMODE(sysarch, I386_SET_IOPERM, &sysarch_arg); #else /* XXXJA: write a test for arm */ FAIL("capmode:no sysarch() test for current architecture"); #endif /* XXXRW: No error return from sync(2) to test. */ return (success); } Index: stable/10/tools/regression/security/cap_test/cap_test_fcntl.c =================================================================== --- stable/10/tools/regression/security/cap_test/cap_test_fcntl.c (revision 280249) +++ stable/10/tools/regression/security/cap_test/cap_test_fcntl.c (revision 280250) @@ -1,117 +1,117 @@ /*- * Copyright (c) 2009-2011 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Test that fcntl works in capability mode. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "cap_test.h" /* A filename->descriptor mapping. */ struct fd { char *f_name; int f_fd; }; /* * Ensure that fcntl() works consistently for both regular file descriptors and * capability-wrapped ones. */ int test_fcntl(void) { int success = PASSED; cap_rights_t rights = CAP_READ | CAP_FCNTL; /* * Open some files of different types, and wrap them in capabilities. */ struct fd files[] = { { "file", open("/etc/passwd", O_RDONLY) }, { "socket", socket(PF_LOCAL, SOCK_STREAM, 0) }, { "SHM", shm_open(SHM_ANON, O_RDWR, 0600) }, }; REQUIRE(files[0].f_fd); REQUIRE(files[1].f_fd); REQUIRE(files[2].f_fd); struct fd caps[] = { { "file cap", cap_new(files[0].f_fd, rights) }, { "socket cap", cap_new(files[1].f_fd, rights) }, { "SHM cap", cap_new(files[2].f_fd, rights) }, }; REQUIRE(caps[0].f_fd); REQUIRE(caps[1].f_fd); REQUIRE(caps[2].f_fd); struct fd all[] = { files[0], caps[0], files[1], caps[1], files[2], caps[2], }; const size_t len = sizeof(all) / sizeof(struct fd); REQUIRE(cap_enter()); /* * Ensure that we can fcntl() all the files that we opened above. */ for (size_t i = 0; i < len; i++) { struct fd f = all[i]; int cap; CHECK_SYSCALL_SUCCEEDS(fcntl, f.f_fd, F_GETFL, 0); REQUIRE(cap = cap_new(f.f_fd, CAP_READ)); if (fcntl(f.f_fd, F_GETFL, 0) == -1) FAIL("Error calling fcntl('%s', F_GETFL)", f.f_name); else CHECK_NOTCAPABLE(fcntl, cap, F_GETFL, 0); } return (success); } Index: stable/10/tools/regression/security/cap_test/cap_test_pdfork.c =================================================================== --- stable/10/tools/regression/security/cap_test/cap_test_pdfork.c (revision 280249) +++ stable/10/tools/regression/security/cap_test/cap_test_pdfork.c (revision 280250) @@ -1,113 +1,113 @@ /*- * Copyright (c) 2009-2011 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Test routines to make sure a variety of system calls are or are not * available in capability mode. The goal is not to see if they work, just * whether or not they return the expected ECAPMODE. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include "cap_test.h" int test_pdfork(void) { struct stat stat; int success = PASSED; int pd, error; pid_t pid; time_t now; //cap_enter(); pid = pdfork(&pd, 0); if (pid < 0) err(-1, "pdfork"); else if (pid == 0) { /* * Child process. * * pd should not be a valid process descriptor. */ error = pdgetpid(pd, &pid); if (error != -1) FAILX("pdgetpid succeeded"); else if (errno != EBADF) FAIL("pdgetpid failed, but errno != EBADF"); exit(success); } /* Parent process. Ensure that [acm]times have been set correctly. */ REQUIRE(fstat(pd, &stat)); now = time(NULL); CHECK(now != (time_t)-1); CHECK(now >= stat.st_birthtime); CHECK((now - stat.st_birthtime) < 2); CHECK(stat.st_birthtime == stat.st_atime); CHECK(stat.st_atime == stat.st_ctime); CHECK(stat.st_ctime == stat.st_mtime); /* Wait for the child to finish. */ error = pdgetpid(pd, &pid); CHECK(error == 0); CHECK(pid > 0); int status; while (waitpid(pid, &status, 0) != pid) {} if ((success == PASSED) && WIFEXITED(status)) success = WEXITSTATUS(status); else success = FAILED; return (success); } Index: stable/10/tools/regression/security/cap_test/cap_test_pdkill.c =================================================================== --- stable/10/tools/regression/security/cap_test/cap_test_pdkill.c (revision 280249) +++ stable/10/tools/regression/security/cap_test/cap_test_pdkill.c (revision 280250) @@ -1,101 +1,101 @@ /*- * Copyright (c) 2009-2011 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Test routines to make sure a variety of system calls are or are not * available in capability mode. The goal is not to see if they work, just * whether or not they return the expected ECAPMODE. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include "cap_test.h" void handle_signal(int); void handle_signal(int sig) { exit(PASSED); } int test_pdkill(void) { int success = PASSED; int pd, error; pid_t pid; //cap_enter(); error = pdfork(&pd, 0); if (error < 0) err(-1, "pdfork"); else if (error == 0) { signal(SIGINT, handle_signal); sleep(3600); exit(FAILED); } /* Parent process; find the child's PID (we'll need it later). */ error = pdgetpid(pd, &pid); if (error != 0) FAIL("pdgetpid"); /* Kill the child! */ usleep(100); error = pdkill(pd, SIGINT); if (error != 0) FAIL("pdkill"); /* Make sure the child finished properly. */ int status; while (waitpid(pid, &status, 0) != pid) {} if ((success == PASSED) && WIFEXITED(status)) success = WEXITSTATUS(status); else success = FAILED; return (success); } Index: stable/10/tools/regression/security/cap_test/cap_test_relative.c =================================================================== --- stable/10/tools/regression/security/cap_test/cap_test_relative.c (revision 280249) +++ stable/10/tools/regression/security/cap_test/cap_test_relative.c (revision 280250) @@ -1,152 +1,152 @@ /*- * Copyright (c) 2009-2011 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include "cap_test.h" /* * Test openat(2) in a variety of sitations to ensure that it obeys Capsicum * "strict relative" rules: * * 1. Use strict relative lookups in capability mode or when operating * relative to a capability. * 2. When performing strict relative lookups, absolute paths (including * symlinks to absolute paths) are not allowed, nor are paths containing * '..' components. */ int test_relative(void) { int success = PASSED; int fd, etc, etc_cap, etc_cap_ro, etc_cap_base, etc_cap_all; cap_rights_t baserights = CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP; cap_rights_t rights; REQUIRE(etc = open("/etc/", O_RDONLY)); CHECK_SYSCALL_SUCCEEDS(cap_getrights, etc, &rights); CHECK_RIGHTS(rights, CAP_ALL); MAKE_CAPABILITY(etc_cap, etc, CAP_READ); MAKE_CAPABILITY(etc_cap_ro, etc, CAP_READ | CAP_LOOKUP); MAKE_CAPABILITY(etc_cap_base, etc, baserights); MAKE_CAPABILITY(etc_cap_all, etc, CAP_MASK_VALID); /* * openat(2) with regular file descriptors in non-capability mode * should Just Work (tm). */ CHECK_SYSCALL_SUCCEEDS(openat, etc, "/etc/passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, AT_FDCWD, "/etc/passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, etc, "passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, etc, "../etc/passwd", O_RDONLY); /* * Lookups relative to capabilities should be strictly relative. * * When not in capability mode, we don't actually require CAP_LOOKUP. */ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_ro, "passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_base, "passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_all, "passwd", O_RDONLY); CHECK_NOTCAPABLE(openat, etc_cap_ro, "../etc/passwd", O_RDONLY); CHECK_NOTCAPABLE(openat, etc_cap_base, "../etc/passwd", O_RDONLY); /* * This requires discussion: do we treat a capability with * CAP_MASK_VALID *exactly* like a non-capability file descriptor * (currently, the implementation says yes)? */ CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_all, "../etc/passwd", O_RDONLY); /* * A file opened relative to a capability should itself be a capability. */ CHECK_SYSCALL_SUCCEEDS(cap_getrights, etc_cap_base, &rights); REQUIRE(fd = openat(etc_cap_base, "passwd", O_RDONLY)); CHECK_SYSCALL_SUCCEEDS(cap_getrights, fd, &rights); CHECK_RIGHTS(rights, baserights); /* * Enter capability mode; now ALL lookups are strictly relative. */ REQUIRE(cap_enter()); /* * Relative lookups on regular files or capabilities with CAP_LOOKUP * ought to succeed. */ CHECK_SYSCALL_SUCCEEDS(openat, etc, "passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_ro, "passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_base, "passwd", O_RDONLY); CHECK_SYSCALL_SUCCEEDS(openat, etc_cap_all, "passwd", O_RDONLY); /* * Lookup relative to capabilities without CAP_LOOKUP should fail. */ CHECK_NOTCAPABLE(openat, etc_cap, "passwd", O_RDONLY); /* * Absolute lookups should fail. */ CHECK_CAPMODE(openat, AT_FDCWD, "/etc/passwd", O_RDONLY); CHECK_NOTCAPABLE(openat, etc, "/etc/passwd", O_RDONLY); /* * Lookups containing '..' should fail in capability mode. */ CHECK_NOTCAPABLE(openat, etc, "../etc/passwd", O_RDONLY); CHECK_NOTCAPABLE(openat, etc_cap_ro, "../etc/passwd", O_RDONLY); CHECK_NOTCAPABLE(openat, etc_cap_base, "../etc/passwd", O_RDONLY); REQUIRE(fd = openat(etc, "passwd", O_RDONLY)); CHECK_SYSCALL_SUCCEEDS(cap_getrights, fd, &rights); /* * A file opened relative to a capability should itself be a capability. */ REQUIRE(fd = openat(etc_cap_base, "passwd", O_RDONLY)); CHECK_SYSCALL_SUCCEEDS(cap_getrights, fd, &rights); CHECK_RIGHTS(rights, baserights); return success; } Index: stable/10/tools/regression/security/cap_test/cap_test_sysctl.c =================================================================== --- stable/10/tools/regression/security/cap_test/cap_test_sysctl.c (revision 280249) +++ stable/10/tools/regression/security/cap_test/cap_test_sysctl.c (revision 280250) @@ -1,67 +1,67 @@ /*- * Copyright (c) 2008-2011 Robert N. M. Watson * Copyright (c) 2011 Jonathan Anderson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Test that various sysctls are (and aren't) available on capability mode. */ #include __FBSDID("$FreeBSD$"); #include -#include +#include #include #include #include #include #include #include #include #include "cap_test.h" /* * Certain sysctls are permitted in capability mode, but most are not. Test * for the ones that should be, and try one or two that shouldn't. */ int test_sysctl(void) { int i, oid[2]; int success = PASSED; size_t len; oid[0] = CTL_KERN; oid[1] = KERN_OSRELDATE; len = sizeof(i); CHECK(sysctl(oid, 2, &i, &len, NULL, 0) == 0); return (success); } Index: stable/10/usr.bin/kdump/kdump.c =================================================================== --- stable/10/usr.bin/kdump/kdump.c (revision 280249) +++ stable/10/usr.bin/kdump/kdump.c (revision 280250) @@ -1,1957 +1,1957 @@ /*- * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)kdump.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #define _KERNEL extern int errno; #include #undef _KERNEL #include -#include +#include #include #define _KERNEL #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #include #ifdef IPX #include #include #endif #ifdef NETATALK #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ktrace.h" #include "kdump_subr.h" u_int abidump(struct ktr_header *); int fetchprocinfo(struct ktr_header *, u_int *); int fread_tail(void *, int, int); void dumpheader(struct ktr_header *); void ktrsyscall(struct ktr_syscall *, u_int); void ktrsysret(struct ktr_sysret *, u_int); void ktrnamei(char *, int); void hexdump(char *, int, int); void visdump(char *, int, int); void ktrgenio(struct ktr_genio *, int); void ktrpsig(struct ktr_psig *); void ktrcsw(struct ktr_csw *); void ktrcsw_old(struct ktr_csw_old *); void ktruser_malloc(void *); void ktruser_rtld(int, void *); void ktruser(int, void *); void ktrcaprights(cap_rights_t *); void ktrsockaddr(struct sockaddr *); void ktrstat(struct stat *); void ktrstruct(char *, size_t); void ktrcapfail(struct ktr_cap_fail *); void ktrfault(struct ktr_fault *); void ktrfaultend(struct ktr_faultend *); void limitfd(int fd); void usage(void); void ioctlname(unsigned long, int); extern const char *signames[], *syscallnames[]; extern int nsyscalls; static int timestamp, decimal, fancy = 1, suppressdata, tail, threads, maxdata, resolv = 0, abiflag = 0, syscallno = 0; static const char *tracefile = DEF_TRACEFILE; static struct ktr_header ktr_header; #define TIME_FORMAT "%b %e %T %Y" #define eqs(s1, s2) (strcmp((s1), (s2)) == 0) #define print_number(i,n,c) do { \ if (decimal) \ printf("%c%jd", c, (intmax_t)*i); \ else \ printf("%c%#jx", c, (uintmax_t)(u_register_t)*i); \ i++; \ n--; \ c = ','; \ } while (0) #if defined(__amd64__) || defined(__i386__) void linux_ktrsyscall(struct ktr_syscall *); void linux_ktrsysret(struct ktr_sysret *); extern const char *linux_syscallnames[]; #include static int nlinux_syscalls = sizeof(linux_syscallnames) / \ sizeof(linux_syscallnames[0]); /* * from linux.h * Linux syscalls return negative errno's, we do positive and map them */ static int bsd_to_linux_errno[ELAST + 1] = { -0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -35, -12, -13, -14, -15, -16, -17, -18, -19, -20, -21, -22, -23, -24, -25, -26, -27, -28, -29, -30, -31, -32, -33, -34, -11,-115,-114, -88, -89, -90, -91, -92, -93, -94, -95, -96, -97, -98, -99, -100,-101,-102,-103,-104,-105,-106,-107,-108,-109, -110,-111, -40, -36,-112,-113, -39, -11, -87,-122, -116, -66, -6, -6, -6, -6, -6, -37, -38, -9, -6, -6, -43, -42, -75,-125, -84, -95, -16, -74, -72, -67, -71 }; #endif struct proc_info { TAILQ_ENTRY(proc_info) info; u_int sv_flags; pid_t pid; }; static TAILQ_HEAD(trace_procs, proc_info) trace_procs; static void strerror_init(void) { /* * Cache NLS data before entering capability mode. * XXXPJD: There should be strerror_init() and strsignal_init() in libc. */ (void)catopen("libc", NL_CAT_LOCALE); } static void localtime_init(void) { time_t ltime; /* * Allow localtime(3) to cache /etc/localtime content before entering * capability mode. * XXXPJD: There should be localtime_init() in libc. */ (void)time(<ime); (void)localtime(<ime); } int main(int argc, char *argv[]) { int ch, ktrlen, size; void *m; int trpoints = ALL_POINTS; int drop_logged; pid_t pid = 0; u_int sv_flags; setlocale(LC_CTYPE, ""); while ((ch = getopt(argc,argv,"f:dElm:np:AHRrSsTt:")) != -1) switch (ch) { case 'A': abiflag = 1; break; case 'f': tracefile = optarg; break; case 'd': decimal = 1; break; case 'l': tail = 1; break; case 'm': maxdata = atoi(optarg); break; case 'n': fancy = 0; break; case 'p': pid = atoi(optarg); break; case 'r': resolv = 1; break; case 'S': syscallno = 1; break; case 's': suppressdata = 1; break; case 'E': timestamp = 3; /* elapsed timestamp */ break; case 'H': threads = 1; break; case 'R': timestamp = 2; /* relative timestamp */ break; case 'T': timestamp = 1; break; case 't': trpoints = getpoints(optarg); if (trpoints < 0) errx(1, "unknown trace point in %s", optarg); break; default: usage(); } if (argc > optind) usage(); m = malloc(size = 1025); if (m == NULL) errx(1, "%s", strerror(ENOMEM)); if (!freopen(tracefile, "r", stdin)) err(1, "%s", tracefile); strerror_init(); localtime_init(); if (resolv == 0) { if (cap_enter() < 0 && errno != ENOSYS) err(1, "unable to enter capability mode"); } limitfd(STDIN_FILENO); limitfd(STDOUT_FILENO); limitfd(STDERR_FILENO); TAILQ_INIT(&trace_procs); drop_logged = 0; while (fread_tail(&ktr_header, sizeof(struct ktr_header), 1)) { if (ktr_header.ktr_type & KTR_DROP) { ktr_header.ktr_type &= ~KTR_DROP; if (!drop_logged && threads) { printf( "%6jd %6jd %-8.*s Events dropped.\n", (intmax_t)ktr_header.ktr_pid, ktr_header.ktr_tid > 0 ? (intmax_t)ktr_header.ktr_tid : 0, MAXCOMLEN, ktr_header.ktr_comm); drop_logged = 1; } else if (!drop_logged) { printf("%6jd %-8.*s Events dropped.\n", (intmax_t)ktr_header.ktr_pid, MAXCOMLEN, ktr_header.ktr_comm); drop_logged = 1; } } if (trpoints & (1< size) { m = realloc(m, ktrlen+1); if (m == NULL) errx(1, "%s", strerror(ENOMEM)); size = ktrlen; } if (ktrlen && fread_tail(m, ktrlen, 1) == 0) errx(1, "data too short"); if (fetchprocinfo(&ktr_header, (u_int *)m) != 0) continue; sv_flags = abidump(&ktr_header); if (pid && ktr_header.ktr_pid != pid && ktr_header.ktr_tid != pid) continue; if ((trpoints & (1<ktr_type) { case KTR_PROCCTOR: TAILQ_FOREACH(pi, &trace_procs, info) { if (pi->pid == kth->ktr_pid) { TAILQ_REMOVE(&trace_procs, pi, info); break; } } pi = malloc(sizeof(struct proc_info)); if (pi == NULL) errx(1, "%s", strerror(ENOMEM)); pi->sv_flags = *flags; pi->pid = kth->ktr_pid; TAILQ_INSERT_TAIL(&trace_procs, pi, info); return (1); case KTR_PROCDTOR: TAILQ_FOREACH(pi, &trace_procs, info) { if (pi->pid == kth->ktr_pid) { TAILQ_REMOVE(&trace_procs, pi, info); free(pi); break; } } return (1); } return (0); } u_int abidump(struct ktr_header *kth) { struct proc_info *pi; const char *abi; const char *arch; u_int flags = 0; TAILQ_FOREACH(pi, &trace_procs, info) { if (pi->pid == kth->ktr_pid) { flags = pi->sv_flags; break; } } if (abiflag == 0) return (flags); switch (flags & SV_ABI_MASK) { case SV_ABI_LINUX: abi = "L"; break; case SV_ABI_FREEBSD: abi = "F"; break; default: abi = "U"; break; } if (flags != 0) { if (flags & SV_LP64) arch = "64"; else arch = "32"; } else arch = "00"; printf("%s%s ", abi, arch); return (flags); } void dumpheader(struct ktr_header *kth) { static char unknown[64]; static struct timeval prevtime, temp; const char *type; switch (kth->ktr_type) { case KTR_SYSCALL: type = "CALL"; break; case KTR_SYSRET: type = "RET "; break; case KTR_NAMEI: type = "NAMI"; break; case KTR_GENIO: type = "GIO "; break; case KTR_PSIG: type = "PSIG"; break; case KTR_CSW: type = "CSW "; break; case KTR_USER: type = "USER"; break; case KTR_STRUCT: type = "STRU"; break; case KTR_SYSCTL: type = "SCTL"; break; case KTR_PROCCTOR: /* FALLTHROUGH */ case KTR_PROCDTOR: return; case KTR_CAPFAIL: type = "CAP "; break; case KTR_FAULT: type = "PFLT"; break; case KTR_FAULTEND: type = "PRET"; break; default: sprintf(unknown, "UNKNOWN(%d)", kth->ktr_type); type = unknown; } /* * The ktr_tid field was previously the ktr_buffer field, which held * the kernel pointer value for the buffer associated with data * following the record header. It now holds a threadid, but only * for trace files after the change. Older trace files still contain * kernel pointers. Detect this and suppress the results by printing * negative tid's as 0. */ if (threads) printf("%6jd %6jd %-8.*s ", (intmax_t)kth->ktr_pid, kth->ktr_tid > 0 ? (intmax_t)kth->ktr_tid : 0, MAXCOMLEN, kth->ktr_comm); else printf("%6jd %-8.*s ", (intmax_t)kth->ktr_pid, MAXCOMLEN, kth->ktr_comm); if (timestamp) { if (timestamp == 3) { if (prevtime.tv_sec == 0) prevtime = kth->ktr_time; timevalsub(&kth->ktr_time, &prevtime); } if (timestamp == 2) { temp = kth->ktr_time; timevalsub(&kth->ktr_time, &prevtime); prevtime = temp; } printf("%jd.%06ld ", (intmax_t)kth->ktr_time.tv_sec, kth->ktr_time.tv_usec); } printf("%s ", type); } #include #define KTRACE #include #undef KTRACE int nsyscalls = sizeof (syscallnames) / sizeof (syscallnames[0]); void ktrsyscall(struct ktr_syscall *ktr, u_int flags) { int narg = ktr->ktr_narg; register_t *ip; intmax_t arg; if ((flags != 0 && ((flags & SV_ABI_MASK) != SV_ABI_FREEBSD)) || (ktr->ktr_code >= nsyscalls || ktr->ktr_code < 0)) printf("[%d]", ktr->ktr_code); else { printf("%s", syscallnames[ktr->ktr_code]); if (syscallno) printf("[%d]", ktr->ktr_code); } ip = &ktr->ktr_args[0]; if (narg) { char c = '('; if (fancy && (flags == 0 || (flags & SV_ABI_MASK) == SV_ABI_FREEBSD)) { switch (ktr->ktr_code) { case SYS_bindat: case SYS_connectat: case SYS_faccessat: case SYS_fchmodat: case SYS_fchownat: case SYS_fstatat: case SYS_futimesat: case SYS_linkat: case SYS_mkdirat: case SYS_mkfifoat: case SYS_mknodat: case SYS_openat: case SYS_readlinkat: case SYS_renameat: case SYS_unlinkat: putchar('('); atfdname(*ip, decimal); c = ','; ip++; narg--; break; } switch (ktr->ktr_code) { case SYS_ioctl: { print_number(ip, narg, c); putchar(c); ioctlname(*ip, decimal); c = ','; ip++; narg--; break; } case SYS_ptrace: putchar('('); ptraceopname(*ip); c = ','; ip++; narg--; break; case SYS_access: case SYS_eaccess: case SYS_faccessat: print_number(ip, narg, c); putchar(','); accessmodename(*ip); ip++; narg--; break; case SYS_open: case SYS_openat: print_number(ip, narg, c); putchar(','); flagsandmodename(ip[0], ip[1], decimal); ip += 2; narg -= 2; break; case SYS_wait4: print_number(ip, narg, c); print_number(ip, narg, c); /* * A flags value of zero is valid for * wait4() but not for wait6(), so * handle zero special here. */ if (*ip == 0) { print_number(ip, narg, c); } else { putchar(','); wait6optname(*ip); ip++; narg--; } break; case SYS_wait6: putchar('('); idtypename(*ip, decimal); c = ','; ip++; narg--; print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); wait6optname(*ip); ip++; narg--; break; case SYS_chmod: case SYS_fchmod: case SYS_lchmod: print_number(ip, narg, c); putchar(','); modename(*ip); ip++; narg--; break; case SYS_mknod: case SYS_mknodat: print_number(ip, narg, c); putchar(','); modename(*ip); ip++; narg--; break; case SYS_getfsstat: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); getfsstatflagsname(*ip); ip++; narg--; break; case SYS_mount: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); mountflagsname(*ip); ip++; narg--; break; case SYS_unmount: print_number(ip, narg, c); putchar(','); mountflagsname(*ip); ip++; narg--; break; case SYS_recvmsg: case SYS_sendmsg: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); sendrecvflagsname(*ip); ip++; narg--; break; case SYS_recvfrom: case SYS_sendto: print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); sendrecvflagsname(*ip); ip++; narg--; break; case SYS_chflags: case SYS_fchflags: case SYS_lchflags: print_number(ip, narg, c); putchar(','); modename(*ip); ip++; narg--; break; case SYS_kill: print_number(ip, narg, c); putchar(','); signame(*ip); ip++; narg--; break; case SYS_reboot: putchar('('); rebootoptname(*ip); ip++; narg--; break; case SYS_umask: putchar('('); modename(*ip); ip++; narg--; break; case SYS_msync: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); msyncflagsname(*ip); ip++; narg--; break; #ifdef SYS_freebsd6_mmap case SYS_freebsd6_mmap: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); mmapprotname(*ip); putchar(','); ip++; narg--; mmapflagsname(*ip); ip++; narg--; break; #endif case SYS_mmap: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); mmapprotname(*ip); putchar(','); ip++; narg--; mmapflagsname(*ip); ip++; narg--; break; case SYS_mprotect: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); mmapprotname(*ip); ip++; narg--; break; case SYS_madvise: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); madvisebehavname(*ip); ip++; narg--; break; case SYS_setpriority: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); prioname(*ip); ip++; narg--; break; case SYS_fcntl: print_number(ip, narg, c); putchar(','); fcntlcmdname(ip[0], ip[1], decimal); ip += 2; narg -= 2; break; case SYS_socket: { int sockdomain; putchar('('); sockdomain = *ip; sockdomainname(sockdomain); ip++; narg--; putchar(','); socktypenamewithflags(*ip); ip++; narg--; if (sockdomain == PF_INET || sockdomain == PF_INET6) { putchar(','); sockipprotoname(*ip); ip++; narg--; } c = ','; break; } case SYS_setsockopt: case SYS_getsockopt: print_number(ip, narg, c); putchar(','); sockoptlevelname(*ip, decimal); if (*ip == SOL_SOCKET) { ip++; narg--; putchar(','); sockoptname(*ip); } ip++; narg--; break; #ifdef SYS_freebsd6_lseek case SYS_freebsd6_lseek: print_number(ip, narg, c); /* Hidden 'pad' argument, not in lseek(2) */ print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); whencename(*ip); ip++; narg--; break; #endif case SYS_lseek: print_number(ip, narg, c); /* Hidden 'pad' argument, not in lseek(2) */ print_number(ip, narg, c); putchar(','); whencename(*ip); ip++; narg--; break; case SYS_flock: print_number(ip, narg, c); putchar(','); flockname(*ip); ip++; narg--; break; case SYS_mkfifo: case SYS_mkfifoat: case SYS_mkdir: case SYS_mkdirat: print_number(ip, narg, c); putchar(','); modename(*ip); ip++; narg--; break; case SYS_shutdown: print_number(ip, narg, c); putchar(','); shutdownhowname(*ip); ip++; narg--; break; case SYS_socketpair: putchar('('); sockdomainname(*ip); ip++; narg--; putchar(','); socktypenamewithflags(*ip); ip++; narg--; c = ','; break; case SYS_getrlimit: case SYS_setrlimit: putchar('('); rlimitname(*ip); ip++; narg--; c = ','; break; case SYS_quotactl: print_number(ip, narg, c); putchar(','); quotactlname(*ip); ip++; narg--; c = ','; break; case SYS_nfssvc: putchar('('); nfssvcname(*ip); ip++; narg--; c = ','; break; case SYS_rtprio: putchar('('); rtprioname(*ip); ip++; narg--; c = ','; break; case SYS___semctl: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); semctlname(*ip); ip++; narg--; break; case SYS_semget: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); semgetname(*ip); ip++; narg--; break; case SYS_msgctl: print_number(ip, narg, c); putchar(','); shmctlname(*ip); ip++; narg--; break; case SYS_shmat: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); shmatname(*ip); ip++; narg--; break; case SYS_shmctl: print_number(ip, narg, c); putchar(','); shmctlname(*ip); ip++; narg--; break; case SYS_shm_open: print_number(ip, narg, c); putchar(','); flagsname(ip[0]); printf(",0%o", (unsigned int)ip[1]); ip += 3; narg -= 3; break; case SYS_minherit: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); minheritname(*ip); ip++; narg--; break; case SYS_rfork: putchar('('); rforkname(*ip); ip++; narg--; c = ','; break; case SYS_lio_listio: putchar('('); lio_listioname(*ip); ip++; narg--; c = ','; break; case SYS_mlockall: putchar('('); mlockallname(*ip); ip++; narg--; break; case SYS_sched_setscheduler: print_number(ip, narg, c); putchar(','); schedpolicyname(*ip); ip++; narg--; break; case SYS_sched_get_priority_max: case SYS_sched_get_priority_min: putchar('('); schedpolicyname(*ip); ip++; narg--; break; case SYS_sendfile: print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); sendfileflagsname(*(int *)ip); ip++; narg--; break; case SYS_kldsym: print_number(ip, narg, c); putchar(','); kldsymcmdname(*ip); ip++; narg--; break; case SYS_sigprocmask: putchar('('); sigprocmaskhowname(*ip); ip++; narg--; c = ','; break; case SYS___acl_get_file: case SYS___acl_set_file: case SYS___acl_get_fd: case SYS___acl_set_fd: case SYS___acl_delete_file: case SYS___acl_delete_fd: case SYS___acl_aclcheck_file: case SYS___acl_aclcheck_fd: case SYS___acl_get_link: case SYS___acl_set_link: case SYS___acl_delete_link: case SYS___acl_aclcheck_link: print_number(ip, narg, c); putchar(','); acltypename(*ip); ip++; narg--; break; case SYS_sigaction: putchar('('); signame(*ip); ip++; narg--; c = ','; break; case SYS_extattrctl: print_number(ip, narg, c); putchar(','); extattrctlname(*ip); ip++; narg--; break; case SYS_nmount: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); mountflagsname(*ip); ip++; narg--; break; case SYS_thr_create: print_number(ip, narg, c); print_number(ip, narg, c); putchar(','); thrcreateflagsname(*ip); ip++; narg--; break; case SYS_thr_kill: print_number(ip, narg, c); putchar(','); signame(*ip); ip++; narg--; break; case SYS_kldunloadf: print_number(ip, narg, c); putchar(','); kldunloadfflagsname(*ip); ip++; narg--; break; case SYS_linkat: case SYS_renameat: case SYS_symlinkat: print_number(ip, narg, c); putchar(','); atfdname(*ip, decimal); ip++; narg--; break; case SYS_cap_fcntls_limit: print_number(ip, narg, c); putchar(','); arg = *ip; ip++; narg--; capfcntlname(arg); break; case SYS_posix_fadvise: print_number(ip, narg, c); print_number(ip, narg, c); print_number(ip, narg, c); (void)putchar(','); fadvisebehavname((int)*ip); ip++; narg--; break; case SYS_procctl: putchar('('); idtypename(*ip, decimal); c = ','; ip++; narg--; print_number(ip, narg, c); putchar(','); procctlcmdname(*ip); ip++; narg--; break; case SYS__umtx_op: print_number(ip, narg, c); putchar(','); umtxopname(*ip); switch (*ip) { case UMTX_OP_CV_WAIT: ip++; narg--; putchar(','); umtxcvwaitflags(*ip); break; case UMTX_OP_RW_RDLOCK: ip++; narg--; putchar(','); umtxrwlockflags(*ip); break; } ip++; narg--; } } while (narg > 0) { print_number(ip, narg, c); } putchar(')'); } putchar('\n'); } void ktrsysret(struct ktr_sysret *ktr, u_int flags) { register_t ret = ktr->ktr_retval; int error = ktr->ktr_error; int code = ktr->ktr_code; if ((flags != 0 && ((flags & SV_ABI_MASK) != SV_ABI_FREEBSD)) || (code >= nsyscalls || code < 0)) printf("[%d] ", code); else { printf("%s", syscallnames[code]); if (syscallno) printf("[%d]", code); printf(" "); } if (error == 0) { if (fancy) { printf("%ld", (long)ret); if (ret < 0 || ret > 9) printf("/%#lx", (unsigned long)ret); } else { if (decimal) printf("%ld", (long)ret); else printf("%#lx", (unsigned long)ret); } } else if (error == ERESTART) printf("RESTART"); else if (error == EJUSTRETURN) printf("JUSTRETURN"); else { printf("-1 errno %d", ktr->ktr_error); if (fancy) printf(" %s", strerror(ktr->ktr_error)); } putchar('\n'); } void ktrnamei(char *cp, int len) { printf("\"%.*s\"\n", len, cp); } void hexdump(char *p, int len, int screenwidth) { int n, i; int width; width = 0; do { width += 2; i = 13; /* base offset */ i += (width / 2) + 1; /* spaces every second byte */ i += (width * 2); /* width of bytes */ i += 3; /* " |" */ i += width; /* each byte */ i += 1; /* "|" */ } while (i < screenwidth); width -= 2; for (n = 0; n < len; n += width) { for (i = n; i < n + width; i++) { if ((i % width) == 0) { /* beginning of line */ printf(" 0x%04x", i); } if ((i % 2) == 0) { printf(" "); } if (i < len) printf("%02x", p[i] & 0xff); else printf(" "); } printf(" |"); for (i = n; i < n + width; i++) { if (i >= len) break; if (p[i] >= ' ' && p[i] <= '~') printf("%c", p[i]); else printf("."); } printf("|\n"); } if ((i % width) != 0) printf("\n"); } void visdump(char *dp, int datalen, int screenwidth) { int col = 0; char *cp; int width; char visbuf[5]; printf(" \""); col = 8; for (;datalen > 0; datalen--, dp++) { vis(visbuf, *dp, VIS_CSTYLE, *(dp+1)); cp = visbuf; /* * Keep track of printables and * space chars (like fold(1)). */ if (col == 0) { putchar('\t'); col = 8; } switch(*cp) { case '\n': col = 0; putchar('\n'); continue; case '\t': width = 8 - (col&07); break; default: width = strlen(cp); } if (col + width > (screenwidth-2)) { printf("\\\n\t"); col = 8; } col += width; do { putchar(*cp++); } while (*cp); } if (col == 0) printf(" "); printf("\"\n"); } void ktrgenio(struct ktr_genio *ktr, int len) { int datalen = len - sizeof (struct ktr_genio); char *dp = (char *)ktr + sizeof (struct ktr_genio); static int screenwidth = 0; int i, binary; printf("fd %d %s %d byte%s\n", ktr->ktr_fd, ktr->ktr_rw == UIO_READ ? "read" : "wrote", datalen, datalen == 1 ? "" : "s"); if (suppressdata) return; if (screenwidth == 0) { struct winsize ws; if (fancy && ioctl(fileno(stderr), TIOCGWINSZ, &ws) != -1 && ws.ws_col > 8) screenwidth = ws.ws_col; else screenwidth = 80; } if (maxdata && datalen > maxdata) datalen = maxdata; for (i = 0, binary = 0; i < datalen && binary == 0; i++) { if (dp[i] >= 32 && dp[i] < 127) continue; if (dp[i] == 10 || dp[i] == 13 || dp[i] == 0 || dp[i] == 9) continue; binary = 1; } if (binary) hexdump(dp, datalen, screenwidth); else visdump(dp, datalen, screenwidth); } const char *signames[] = { "NULL", "HUP", "INT", "QUIT", "ILL", "TRAP", "IOT", /* 1 - 6 */ "EMT", "FPE", "KILL", "BUS", "SEGV", "SYS", /* 7 - 12 */ "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", /* 13 - 18 */ "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU", /* 19 - 24 */ "XFSZ", "VTALRM", "PROF", "WINCH", "29", "USR1", /* 25 - 30 */ "USR2", NULL, /* 31 - 32 */ }; void ktrpsig(struct ktr_psig *psig) { if (psig->signo > 0 && psig->signo < NSIG) printf("SIG%s ", signames[psig->signo]); else printf("SIG %d ", psig->signo); if (psig->action == SIG_DFL) { printf("SIG_DFL code="); sigcodename(psig->signo, psig->code); putchar('\n'); } else { printf("caught handler=0x%lx mask=0x%x code=", (u_long)psig->action, psig->mask.__bits[0]); sigcodename(psig->signo, psig->code); putchar('\n'); } } void ktrcsw_old(struct ktr_csw_old *cs) { printf("%s %s\n", cs->out ? "stop" : "resume", cs->user ? "user" : "kernel"); } void ktrcsw(struct ktr_csw *cs) { printf("%s %s \"%s\"\n", cs->out ? "stop" : "resume", cs->user ? "user" : "kernel", cs->wmesg); } #define UTRACE_DLOPEN_START 1 #define UTRACE_DLOPEN_STOP 2 #define UTRACE_DLCLOSE_START 3 #define UTRACE_DLCLOSE_STOP 4 #define UTRACE_LOAD_OBJECT 5 #define UTRACE_UNLOAD_OBJECT 6 #define UTRACE_ADD_RUNDEP 7 #define UTRACE_PRELOAD_FINISHED 8 #define UTRACE_INIT_CALL 9 #define UTRACE_FINI_CALL 10 struct utrace_rtld { char sig[4]; /* 'RTLD' */ int event; void *handle; void *mapbase; size_t mapsize; int refcnt; char name[MAXPATHLEN]; }; void ktruser_rtld(int len, void *p) { struct utrace_rtld *ut = p; unsigned char *cp; void *parent; int mode; switch (ut->event) { case UTRACE_DLOPEN_START: mode = ut->refcnt; printf("dlopen(%s, ", ut->name); switch (mode & RTLD_MODEMASK) { case RTLD_NOW: printf("RTLD_NOW"); break; case RTLD_LAZY: printf("RTLD_LAZY"); break; default: printf("%#x", mode & RTLD_MODEMASK); } if (mode & RTLD_GLOBAL) printf(" | RTLD_GLOBAL"); if (mode & RTLD_TRACE) printf(" | RTLD_TRACE"); if (mode & ~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE)) printf(" | %#x", mode & ~(RTLD_MODEMASK | RTLD_GLOBAL | RTLD_TRACE)); printf(")\n"); break; case UTRACE_DLOPEN_STOP: printf("%p = dlopen(%s) ref %d\n", ut->handle, ut->name, ut->refcnt); break; case UTRACE_DLCLOSE_START: printf("dlclose(%p) (%s, %d)\n", ut->handle, ut->name, ut->refcnt); break; case UTRACE_DLCLOSE_STOP: printf("dlclose(%p) finished\n", ut->handle); break; case UTRACE_LOAD_OBJECT: printf("RTLD: loaded %p @ %p - %p (%s)\n", ut->handle, ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1, ut->name); break; case UTRACE_UNLOAD_OBJECT: printf("RTLD: unloaded %p @ %p - %p (%s)\n", ut->handle, ut->mapbase, (char *)ut->mapbase + ut->mapsize - 1, ut->name); break; case UTRACE_ADD_RUNDEP: parent = ut->mapbase; printf("RTLD: %p now depends on %p (%s, %d)\n", parent, ut->handle, ut->name, ut->refcnt); break; case UTRACE_PRELOAD_FINISHED: printf("RTLD: LD_PRELOAD finished\n"); break; case UTRACE_INIT_CALL: printf("RTLD: init %p for %p (%s)\n", ut->mapbase, ut->handle, ut->name); break; case UTRACE_FINI_CALL: printf("RTLD: fini %p for %p (%s)\n", ut->mapbase, ut->handle, ut->name); break; default: cp = p; cp += 4; len -= 4; printf("RTLD: %d ", len); while (len--) if (decimal) printf(" %d", *cp++); else printf(" %02x", *cp++); printf("\n"); } } struct utrace_malloc { void *p; size_t s; void *r; }; void ktruser_malloc(void *p) { struct utrace_malloc *ut = p; if (ut->p == (void *)(intptr_t)(-1)) printf("malloc_init()\n"); else if (ut->s == 0) printf("free(%p)\n", ut->p); else if (ut->p == NULL) printf("%p = malloc(%zu)\n", ut->r, ut->s); else printf("%p = realloc(%p, %zu)\n", ut->r, ut->p, ut->s); } void ktruser(int len, void *p) { unsigned char *cp; if (len >= 8 && bcmp(p, "RTLD", 4) == 0) { ktruser_rtld(len, p); return; } if (len == sizeof(struct utrace_malloc)) { ktruser_malloc(p); return; } printf("%d ", len); cp = p; while (len--) if (decimal) printf(" %d", *cp++); else printf(" %02x", *cp++); printf("\n"); } void ktrcaprights(cap_rights_t *rightsp) { printf("cap_rights_t "); capname(rightsp); printf("\n"); } void ktrsockaddr(struct sockaddr *sa) { /* TODO: Support additional address families #include struct sockaddr_natm *natm; #include struct sockaddr_nb *nb; */ char addr[64]; /* * note: ktrstruct() has already verified that sa points to a * buffer at least sizeof(struct sockaddr) bytes long and exactly * sa->sa_len bytes long. */ printf("struct sockaddr { "); sockfamilyname(sa->sa_family); printf(", "); #define check_sockaddr_len(n) \ if (sa_##n.s##n##_len < sizeof(struct sockaddr_##n)) { \ printf("invalid"); \ break; \ } switch(sa->sa_family) { case AF_INET: { struct sockaddr_in sa_in; memset(&sa_in, 0, sizeof(sa_in)); memcpy(&sa_in, sa, sa->sa_len); check_sockaddr_len(in); inet_ntop(AF_INET, &sa_in.sin_addr, addr, sizeof addr); printf("%s:%u", addr, ntohs(sa_in.sin_port)); break; } #ifdef NETATALK case AF_APPLETALK: { struct sockaddr_at sa_at; struct netrange *nr; memset(&sa_at, 0, sizeof(sa_at)); memcpy(&sa_at, sa, sa->sa_len); check_sockaddr_len(at); nr = &sa_at.sat_range.r_netrange; printf("%d.%d, %d-%d, %d", ntohs(sa_at.sat_addr.s_net), sa_at.sat_addr.s_node, ntohs(nr->nr_firstnet), ntohs(nr->nr_lastnet), nr->nr_phase); break; } #endif case AF_INET6: { struct sockaddr_in6 sa_in6; memset(&sa_in6, 0, sizeof(sa_in6)); memcpy(&sa_in6, sa, sa->sa_len); check_sockaddr_len(in6); getnameinfo((struct sockaddr *)&sa_in6, sizeof(sa_in6), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); printf("[%s]:%u", addr, htons(sa_in6.sin6_port)); break; } #ifdef IPX case AF_IPX: { struct sockaddr_ipx sa_ipx; memset(&sa_ipx, 0, sizeof(sa_ipx)); memcpy(&sa_ipx, sa, sa->sa_len); check_sockaddr_len(ipx); /* XXX wish we had ipx_ntop */ printf("%s", ipx_ntoa(sa_ipx.sipx_addr)); free(sa_ipx); break; } #endif case AF_UNIX: { struct sockaddr_un sa_un; memset(&sa_un, 0, sizeof(sa_un)); memcpy(&sa_un, sa, sa->sa_len); printf("%.*s", (int)sizeof(sa_un.sun_path), sa_un.sun_path); break; } default: printf("unknown address family"); } printf(" }\n"); } void ktrstat(struct stat *statp) { char mode[12], timestr[PATH_MAX + 4]; struct passwd *pwd; struct group *grp; struct tm *tm; /* * note: ktrstruct() has already verified that statp points to a * buffer exactly sizeof(struct stat) bytes long. */ printf("struct stat {"); printf("dev=%ju, ino=%ju, ", (uintmax_t)statp->st_dev, (uintmax_t)statp->st_ino); if (resolv == 0) printf("mode=0%jo, ", (uintmax_t)statp->st_mode); else { strmode(statp->st_mode, mode); printf("mode=%s, ", mode); } printf("nlink=%ju, ", (uintmax_t)statp->st_nlink); if (resolv == 0 || (pwd = getpwuid(statp->st_uid)) == NULL) printf("uid=%ju, ", (uintmax_t)statp->st_uid); else printf("uid=\"%s\", ", pwd->pw_name); if (resolv == 0 || (grp = getgrgid(statp->st_gid)) == NULL) printf("gid=%ju, ", (uintmax_t)statp->st_gid); else printf("gid=\"%s\", ", grp->gr_name); printf("rdev=%ju, ", (uintmax_t)statp->st_rdev); printf("atime="); if (resolv == 0) printf("%jd", (intmax_t)statp->st_atim.tv_sec); else { tm = localtime(&statp->st_atim.tv_sec); strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); printf("\"%s\"", timestr); } if (statp->st_atim.tv_nsec != 0) printf(".%09ld, ", statp->st_atim.tv_nsec); else printf(", "); printf("stime="); if (resolv == 0) printf("%jd", (intmax_t)statp->st_mtim.tv_sec); else { tm = localtime(&statp->st_mtim.tv_sec); strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); printf("\"%s\"", timestr); } if (statp->st_mtim.tv_nsec != 0) printf(".%09ld, ", statp->st_mtim.tv_nsec); else printf(", "); printf("ctime="); if (resolv == 0) printf("%jd", (intmax_t)statp->st_ctim.tv_sec); else { tm = localtime(&statp->st_ctim.tv_sec); strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); printf("\"%s\"", timestr); } if (statp->st_ctim.tv_nsec != 0) printf(".%09ld, ", statp->st_ctim.tv_nsec); else printf(", "); printf("birthtime="); if (resolv == 0) printf("%jd", (intmax_t)statp->st_birthtim.tv_sec); else { tm = localtime(&statp->st_birthtim.tv_sec); strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); printf("\"%s\"", timestr); } if (statp->st_birthtim.tv_nsec != 0) printf(".%09ld, ", statp->st_birthtim.tv_nsec); else printf(", "); printf("size=%jd, blksize=%ju, blocks=%jd, flags=0x%x", (uintmax_t)statp->st_size, (uintmax_t)statp->st_blksize, (intmax_t)statp->st_blocks, statp->st_flags); printf(" }\n"); } void ktrstruct(char *buf, size_t buflen) { char *name, *data; size_t namelen, datalen; int i; cap_rights_t rights; struct stat sb; struct sockaddr_storage ss; for (name = buf, namelen = 0; namelen < buflen && name[namelen] != '\0'; ++namelen) /* nothing */; if (namelen == buflen) goto invalid; if (name[namelen] != '\0') goto invalid; data = buf + namelen + 1; datalen = buflen - namelen - 1; if (datalen == 0) goto invalid; /* sanity check */ for (i = 0; i < (int)namelen; ++i) if (!isalpha(name[i])) goto invalid; if (strcmp(name, "caprights") == 0) { if (datalen != sizeof(cap_rights_t)) goto invalid; memcpy(&rights, data, datalen); ktrcaprights(&rights); } else if (strcmp(name, "stat") == 0) { if (datalen != sizeof(struct stat)) goto invalid; memcpy(&sb, data, datalen); ktrstat(&sb); } else if (strcmp(name, "sockaddr") == 0) { if (datalen > sizeof(ss)) goto invalid; memcpy(&ss, data, datalen); if (datalen != ss.ss_len) goto invalid; ktrsockaddr((struct sockaddr *)&ss); } else { printf("unknown structure\n"); } return; invalid: printf("invalid record\n"); } void ktrcapfail(struct ktr_cap_fail *ktr) { switch (ktr->cap_type) { case CAPFAIL_NOTCAPABLE: /* operation on fd with insufficient capabilities */ printf("operation requires "); capname(&ktr->cap_needed); printf(", process holds "); capname(&ktr->cap_held); break; case CAPFAIL_INCREASE: /* requested more capabilities than fd already has */ printf("attempt to increase capabilities from "); capname(&ktr->cap_held); printf(" to "); capname(&ktr->cap_needed); break; case CAPFAIL_SYSCALL: /* called restricted syscall */ printf("disallowed system call"); break; case CAPFAIL_LOOKUP: /* used ".." in strict-relative mode */ printf("restricted VFS lookup"); break; default: printf("unknown capability failure: "); capname(&ktr->cap_needed); printf(" "); capname(&ktr->cap_held); break; } printf("\n"); } void ktrfault(struct ktr_fault *ktr) { printf("0x%jx ", (uintmax_t)ktr->vaddr); vmprotname(ktr->type); printf("\n"); } void ktrfaultend(struct ktr_faultend *ktr) { vmresultname(ktr->result); printf("\n"); } #if defined(__amd64__) || defined(__i386__) void linux_ktrsyscall(struct ktr_syscall *ktr) { int narg = ktr->ktr_narg; register_t *ip; if (ktr->ktr_code >= nlinux_syscalls || ktr->ktr_code < 0) printf("[%d]", ktr->ktr_code); else { printf("%s", linux_syscallnames[ktr->ktr_code]); if (syscallno) printf("[%d]", ktr->ktr_code); } ip = &ktr->ktr_args[0]; if (narg) { char c = '('; while (narg > 0) print_number(ip, narg, c); putchar(')'); } putchar('\n'); } void linux_ktrsysret(struct ktr_sysret *ktr) { register_t ret = ktr->ktr_retval; int error = ktr->ktr_error; int code = ktr->ktr_code; if (code >= nlinux_syscalls || code < 0) printf("[%d] ", code); else { printf("%s", linux_syscallnames[code]); if (syscallno) printf("[%d]", code); printf(" "); } if (error == 0) { if (fancy) { printf("%ld", (long)ret); if (ret < 0 || ret > 9) printf("/%#lx", (unsigned long)ret); } else { if (decimal) printf("%ld", (long)ret); else printf("%#lx", (unsigned long)ret); } } else if (error == ERESTART) printf("RESTART"); else if (error == EJUSTRETURN) printf("JUSTRETURN"); else { if (ktr->ktr_error <= ELAST + 1) error = abs(bsd_to_linux_errno[ktr->ktr_error]); else error = 999; printf("-1 errno %d", error); if (fancy) printf(" %s", strerror(ktr->ktr_error)); } putchar('\n'); } #endif void usage(void) { fprintf(stderr, "usage: kdump [-dEnlHRrSsTA] [-f trfile] " "[-m maxdata] [-p pid] [-t trstr]\n"); exit(1); } Index: stable/10/usr.bin/kdump/mksubr =================================================================== --- stable/10/usr.bin/kdump/mksubr (revision 280249) +++ stable/10/usr.bin/kdump/mksubr (revision 280250) @@ -1,761 +1,761 @@ #!/bin/sh # # Copyright (c) 2006 "David Kirchner" . All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # # Generates kdump_subr.c # mkioctls is a special-purpose script, and works fine as it is # now, so it remains independent. The idea behind how it generates # its list was heavily borrowed here. # # Some functions here are automatically generated. This can mean # the user will see unusual kdump output or errors while building # if the underlying .h files are changed significantly. # # Key: # AUTO: Completely auto-generated with either the "or" or the "switch" # method. # AUTO - Special: Generated automatically, but with some extra commands # that the auto_*_type() functions are inappropriate for. # MANUAL: Manually entered and must therefore be manually updated. set -e LC_ALL=C; export LC_ALL if [ -z "$1" ] then echo "usage: sh $0 include-dir" exit 1 fi include_dir=$1 # # Automatically generates a C function that will print out the # numeric input as a pipe-delimited string of the appropriate # #define keys. ex: # S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH # The XOR is necessary to prevent including the "0"-value in every # line. # auto_or_type () { local name grep file name=$1 grep=$2 file=$3 cat <<_EOF_ /* AUTO */ void $name(intmax_t arg) { int or = 0; printf("%#jx<", (uintmax_t)arg); _EOF_ egrep "^#[[:space:]]*define[[:space:]]+"${grep}"[[:space:]]*" \ $include_dir/$file | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tif (!((arg > 0) ^ ((%s) > 0)))\n\t\tif_print_or(arg, %s, or);\n", $i, $i }' cat <<_EOF_ printf(">"); if (or == 0) printf("%jd", arg); } _EOF_ } # # Automatically generates a C function used when the argument # maps to a single, specific #definition # auto_switch_type () { local name grep file name=$1 grep=$2 file=$3 cat <<_EOF_ /* AUTO */ void $name(intmax_t arg) { switch (arg) { _EOF_ egrep "^#[[:space:]]*define[[:space:]]+"${grep}"[[:space:]]*" \ $include_dir/$file | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tcase %s:\n\t\tprintf(\"%s\");\n\t\tbreak;\n", $i, $i }' cat <<_EOF_ default: /* Should not reach */ printf("", arg); } } _EOF_ } # # Automatically generates a C function used when the argument # maps to a #definition # auto_if_type () { local name grep file name=$1 grep=$2 file=$3 cat <<_EOF_ /* AUTO */ void $name(intmax_t arg) { _EOF_ egrep "^#[[:space:]]*define[[:space:]]+"${grep}"[[:space:]]*" \ $include_dir/$file | \ awk '{ printf "\t"; \ if (NR > 1) \ printf "else " ; \ printf "if (arg == %s) \n\t\tprintf(\"%s\");\n", $2, $2 }' cat <<_EOF_ else /* Should not reach */ printf("", arg); } _EOF_ } # C start cat <<_EOF_ #include #include #include #include #include #include #include #include #define _KERNEL #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #define _KERNEL #include #undef _KERNEL #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include #include #include "kdump_subr.h" /* * These are simple support macros. print_or utilizes a variable * defined in the calling function to track whether or not it should * print a logical-OR character ('|') before a string. if_print_or * simply handles the necessary "if" statement used in many lines * of this file. */ #define print_or(str,orflag) do { \\ if (orflag) putchar('|'); else orflag = 1; \\ printf (str); } \\ while (0) #define if_print_or(i,flag,orflag) do { \\ if ((i & flag) == flag) \\ print_or(#flag,orflag); } \\ while (0) /* MANUAL */ void atfdname(int fd, int decimal) { if (fd == AT_FDCWD) printf("AT_FDCWD"); else if (decimal) printf("%d", fd); else printf("%#x", fd); } /* MANUAL */ extern char *signames[]; /* from kdump.c */ void signame(int sig) { if (sig > 0 && sig < NSIG) printf("SIG%s",signames[sig]); else printf("SIG %d", sig); } /* MANUAL */ void semctlname(int cmd) { switch (cmd) { case GETNCNT: printf("GETNCNT"); break; case GETPID: printf("GETPID"); break; case GETVAL: printf("GETVAL"); break; case GETALL: printf("GETALL"); break; case GETZCNT: printf("GETZCNT"); break; case SETVAL: printf("SETVAL"); break; case SETALL: printf("SETALL"); break; case IPC_RMID: printf("IPC_RMID"); break; case IPC_SET: printf("IPC_SET"); break; case IPC_STAT: printf("IPC_STAT"); break; default: /* Should not reach */ printf("", cmd); } } /* MANUAL */ void shmctlname(int cmd) { switch (cmd) { case IPC_RMID: printf("IPC_RMID"); break; case IPC_SET: printf("IPC_SET"); break; case IPC_STAT: printf("IPC_STAT"); break; default: /* Should not reach */ printf("", cmd); } } /* MANUAL */ void semgetname(int flag) { int or = 0; if_print_or(flag, IPC_CREAT, or); if_print_or(flag, IPC_EXCL, or); if_print_or(flag, SEM_R, or); if_print_or(flag, SEM_A, or); if_print_or(flag, (SEM_R>>3), or); if_print_or(flag, (SEM_A>>3), or); if_print_or(flag, (SEM_R>>6), or); if_print_or(flag, (SEM_A>>6), or); } /* * MANUAL * * Only used by SYS_open. Unless O_CREAT is set in flags, the * mode argument is unused (and often bogus and misleading). */ void flagsandmodename(int flags, int mode, int decimal) { flagsname(flags); putchar(','); if ((flags & O_CREAT) == O_CREAT) { modename (mode); } else { if (decimal) { printf("%d", mode); } else { printf("%#x", (unsigned int)mode); } } } /* MANUAL */ void idtypename(idtype_t idtype, int decimal) { switch(idtype) { case P_PID: printf("P_PID"); break; case P_PPID: printf("P_PPID"); break; case P_PGID: printf("P_PGID"); break; case P_SID: printf("P_SID"); break; case P_CID: printf("P_CID"); break; case P_UID: printf("P_UID"); break; case P_GID: printf("P_GID"); break; case P_ALL: printf("P_ALL"); break; case P_LWPID: printf("P_LWPID"); break; case P_TASKID: printf("P_TASKID"); break; case P_PROJID: printf("P_PROJID"); break; case P_POOLID: printf("P_POOLID"); break; case P_JAILID: printf("P_JAILID"); break; case P_CTID: printf("P_CTID"); break; case P_CPUID: printf("P_CPUID"); break; case P_PSETID: printf("P_PSETID"); break; default: if (decimal) { printf("%d", idtype); } else { printf("%#x", idtype); } } } /* * MANUAL * * [g|s]etsockopt's level argument can either be SOL_SOCKET or a value * referring to a line in /etc/protocols . It might be appropriate * to use getprotoent(3) here. */ void sockoptlevelname(int level, int decimal) { if (level == SOL_SOCKET) { printf("SOL_SOCKET"); } else { if (decimal) { printf("%d", level); } else { printf("%#x", (unsigned int)level); } } } /* * MANUAL * * Used for page fault type. Cannot use auto_or_type since the macro * values contain a cast. Also, VM_PROT_NONE has to be handled specially. */ void vmprotname (int type) { int or = 0; if (type == VM_PROT_NONE) { (void)printf("VM_PROT_NONE"); return; } if_print_or(type, VM_PROT_READ, or); if_print_or(type, VM_PROT_WRITE, or); if_print_or(type, VM_PROT_EXECUTE, or); if_print_or(type, VM_PROT_COPY, or); } /* * MANUAL */ void socktypenamewithflags(int type) { if (type & SOCK_CLOEXEC) printf("SOCK_CLOEXEC|"), type &= ~SOCK_CLOEXEC; if (type & SOCK_NONBLOCK) printf("SOCK_NONBLOCK|"), type &= ~SOCK_NONBLOCK; socktypename(type); } _EOF_ auto_or_type "accessmodename" "[A-Z]_OK[[:space:]]+0?x?[0-9A-Fa-f]+" "sys/unistd.h" auto_switch_type "acltypename" "ACL_TYPE_[A-Z4_]+[[:space:]]+0x[0-9]+" "sys/acl.h" -auto_or_type "capfcntlname" "CAP_FCNTL_[A-Z]+[[:space:]]+\(1" "sys/capability.h" +auto_or_type "capfcntlname" "CAP_FCNTL_[A-Z]+[[:space:]]+\(1" "sys/capsicum.h" auto_switch_type "extattrctlname" "EXTATTR_NAMESPACE_[A-Z]+[[:space:]]+0x[0-9]+" "sys/extattr.h" auto_switch_type "fadvisebehavname" "POSIX_FADV_[A-Z]+[[:space:]]+[0-9]+" "sys/fcntl.h" auto_or_type "flagsname" "O_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/fcntl.h" auto_or_type "flockname" "LOCK_[A-Z]+[[:space:]]+0x[0-9]+" "sys/fcntl.h" auto_or_type "getfsstatflagsname" "MNT_[A-Z]+[[:space:]]+[1-9][0-9]*" "sys/mount.h" auto_switch_type "kldsymcmdname" "KLDSYM_[A-Z]+[[:space:]]+[0-9]+" "sys/linker.h" auto_switch_type "kldunloadfflagsname" "LINKER_UNLOAD_[A-Z]+[[:space:]]+[0-9]+" "sys/linker.h" auto_switch_type "lio_listioname" "LIO_(NO)?WAIT[[:space:]]+[0-9]+" "aio.h" auto_switch_type "madvisebehavname" "_?MADV_[A-Z]+[[:space:]]+[0-9]+" "sys/mman.h" auto_switch_type "minheritname" "INHERIT_[A-Z]+[[:space:]]+[0-9]+" "sys/mman.h" auto_or_type "mlockallname" "MCL_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mman.h" auto_or_type "mmapprotname" "PROT_[A-Z]+[[:space:]]+0x[0-9A-Fa-f]+" "sys/mman.h" auto_or_type "modename" "S_[A-Z]+[[:space:]]+[0-6]{7}" "sys/stat.h" auto_or_type "mountflagsname" "MNT_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mount.h" auto_switch_type "msyncflagsname" "MS_[A-Z]+[[:space:]]+0x[0-9]+" "sys/mman.h" auto_or_type "nfssvcname" "NFSSVC_[A-Z0-9]+[[:space:]]+0x[0-9]+" "nfs/nfssvc.h" auto_switch_type "prioname" "PRIO_[A-Z]+[[:space:]]+[0-9]" "sys/resource.h" auto_switch_type "procctlcmdname" "PROC_[A-Z]+[[:space:]]+[0-9]" "sys/procctl.h" auto_switch_type "ptraceopname" "PT_[[:alnum:]_]+[[:space:]]+[0-9]+" "sys/ptrace.h" auto_switch_type "quotactlname" "Q_[A-Z]+[[:space:]]+0x[0-9]+" "ufs/ufs/quota.h" auto_or_type "rebootoptname" "RB_[A-Z]+[[:space:]]+0x[0-9]+" "sys/reboot.h" auto_or_type "rforkname" "RF[A-Z]+[[:space:]]+\([0-9]+<<[0-9]+\)" "sys/unistd.h" auto_switch_type "rlimitname" "RLIMIT_[A-Z]+[[:space:]]+[0-9]+" "sys/resource.h" auto_switch_type "schedpolicyname" "SCHED_[A-Z]+[[:space:]]+[0-9]+" "sched.h" auto_switch_type "sendfileflagsname" "SF_[A-Z]+[[:space:]]+[0-9]+" "sys/socket.h" auto_or_type "shmatname" "SHM_[A-Z]+[[:space:]]+[0-9]{6}+" "sys/shm.h" auto_switch_type "shutdownhowname" "SHUT_[A-Z]+[[:space:]]+[0-9]+" "sys/socket.h" auto_switch_type "sigbuscodename" "BUS_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h" auto_switch_type "sigchldcodename" "CLD_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h" auto_switch_type "sigfpecodename" "FPE_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h" auto_switch_type "sigprocmaskhowname" "SIG_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h" auto_switch_type "sigillcodename" "ILL_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h" auto_switch_type "sigsegvcodename" "SEGV_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h" auto_switch_type "sigtrapcodename" "TRAP_[A-Z]+[[:space:]]+[0-9]+" "sys/signal.h" auto_if_type "sockdomainname" "PF_[[:alnum:]]+[[:space:]]+" "sys/socket.h" auto_if_type "sockfamilyname" "AF_[[:alnum:]]+[[:space:]]+" "sys/socket.h" auto_if_type "sockipprotoname" "IPPROTO_[[:alnum:]]+[[:space:]]+" "netinet/in.h" auto_switch_type "sockoptname" "SO_[A-Z]+[[:space:]]+0x[0-9]+" "sys/socket.h" auto_switch_type "socktypename" "SOCK_[A-Z]+[[:space:]]+[1-9]+[0-9]*" "sys/socket.h" auto_or_type "thrcreateflagsname" "THR_[A-Z]+[[:space:]]+0x[0-9]+" "sys/thr.h" auto_switch_type "umtxopname" "UMTX_OP_[[:alnum:]_]+[[:space:]]+[0-9]+" "sys/umtx.h" auto_switch_type "vmresultname" "KERN_[A-Z]+[[:space:]]+[0-9]+" "vm/vm_param.h" auto_or_type "wait6optname" "W[A-Z]+[[:space:]]+[0-9]+" "sys/wait.h" auto_switch_type "whencename" "SEEK_[A-Z]+[[:space:]]+[0-9]+" "sys/unistd.h" cat <<_EOF_ /* * AUTO - Special * F_ is used to specify fcntl commands as well as arguments. Both sets are * grouped in fcntl.h, and this awk script grabs the first group. */ void fcntlcmdname(int cmd, int arg, int decimal) { switch (cmd) { _EOF_ egrep "^#[[:space:]]*define[[:space:]]+F_[A-Z0-9_]+[[:space:]]+[0-9]+[[:space:]]*" \ $include_dir/sys/fcntl.h | \ awk 'BEGIN { o=0 } { for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ if (o <= $(i+1)) \ printf "\tcase %s:\n\t\tprintf(\"%s\");\n\t\tbreak;\n", $i, $i; \ else \ exit; \ o = $(i+1) }' cat <<_EOF_ default: /* Should not reach */ printf("", cmd); } putchar(','); if (cmd == F_GETFD || cmd == F_SETFD) { if (arg == FD_CLOEXEC) printf("FD_CLOEXEC"); else if (arg == 0) printf("0"); else { if (decimal) printf("%d", arg); else printf("%#x", (unsigned int)arg); } } else if (cmd == F_SETFL) { flagsname(arg); } else { if (decimal) printf("%d", arg); else printf("%#x", (unsigned int)arg); } } /* * AUTO - Special * * The MAP_ALIGNED flag requires special handling. */ void mmapflagsname(int flags) { int align; int or = 0; printf("%#x<", flags); _EOF_ egrep "^#[[:space:]]*define[[:space:]]+MAP_[A-Z_]+[[:space:]]+0x[0-9A-Fa-f]+[[:space:]]*" \ $include_dir/sys/mman.h | grep -v MAP_ALIGNED | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tif (!((flags > 0) ^ ((%s) > 0)))\n\t\tif_print_or(flags, %s, or);\n", $i, $i }' cat <<_EOF_ #ifdef MAP_32BIT if (!((flags > 0) ^ ((MAP_32BIT) > 0))) if_print_or(flags, MAP_32BIT, or); #endif align = flags & MAP_ALIGNMENT_MASK; if (align != 0) { if (align == MAP_ALIGNED_SUPER) print_or("MAP_ALIGNED_SUPER", or); else { print_or("MAP_ALIGNED", or); printf("(%d)", align >> MAP_ALIGNMENT_SHIFT); } } printf(">"); if (or == 0) printf("%d", flags); } /* * AUTO - Special * * The only reason this is not fully automated is due to the * grep -v RTP_PRIO statement. A better egrep line should * make this capable of being a auto_switch_type() function. */ void rtprioname(int func) { switch (func) { _EOF_ egrep "^#[[:space:]]*define[[:space:]]+RTP_[A-Z]+[[:space:]]+0x[0-9]+[[:space:]]*" \ $include_dir/sys/rtprio.h | grep -v RTP_PRIO | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tcase %s:\n\t\tprintf(\"%s\");\n\t\tbreak;\n", $i, $i }' cat <<_EOF_ default: /* Should not reach */ printf("", func); } } /* * AUTO - Special * * The send and recv functions have a flags argument which can be * set to 0. There is no corresponding #define. The auto_ functions * detect this as "invalid", which is incorrect here. */ void sendrecvflagsname(int flags) { int or = 0; if (flags == 0) { printf("0"); return; } printf("%#x<", flags); _EOF_ egrep "^#[[:space:]]*define[[:space:]]+MSG_[A-Z]+[[:space:]]+0x[0-9]+[[:space:]]*" $include_dir/sys/socket.h | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tif(!((flags>0)^((%s)>0)))\n\t\tif_print_or(flags, %s, or);\n", $i, $i }' cat <<_EOF_ printf(">"); } /* * AUTO - Special * * Check general codes first, then defer to signal-specific codes. */ void sigcodename(int sig, int code) { switch (code) { _EOF_ egrep "^#[[:space:]]*define[[:space:]]+SI_[A-Z]+[[:space:]]+0(x[0-9abcdef]+)?[[:space:]]*" \ $include_dir/sys/signal.h | grep -v SI_UNDEFINED | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tcase %s:\n\t\tprintf(\"%s\");\n\t\tbreak;\n", $i, $i }' cat <<_EOF_ default: switch (sig) { case SIGILL: sigillcodename(code); break; case SIGBUS: sigbuscodename(code); break; case SIGSEGV: sigsegvcodename(code); break; case SIGFPE: sigfpecodename(code); break; case SIGTRAP: sigtrapcodename(code); break; case SIGCHLD: sigchldcodename(code); break; default: printf("", code); } } } /* * AUTO - Special * * Just print 0 as 0. */ void umtxcvwaitflags(intmax_t arg) { int or = 0; if (arg == 0) { printf("0"); return; } printf("%#jx<", (uintmax_t)arg); _EOF_ egrep "^#[[:space:]]*define[[:space:]]+CVWAIT_[A-Z_]+[[:space:]]+0x[0-9]+[[:space:]]*" \ $include_dir/sys/umtx.h | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tif (!((arg > 0) ^ ((%s) > 0)))\n\t\tif_print_or(arg, %s, or);\n", $i, $i }' cat <<_EOF_ printf(">"); if (or == 0) printf("%jd", arg); } /* * AUTO - Special * * Just print 0 as 0. */ void umtxrwlockflags(intmax_t arg) { int or = 0; if (arg == 0) { printf("0"); return; } printf("%#jx<", (uintmax_t)arg); _EOF_ egrep "^#[[:space:]]*define[[:space:]]+URWLOCK_PREFER_READER[[:space:]]+0x[0-9]+[[:space:]]*" \ $include_dir/sys/umtx.h | \ awk '{ for (i = 1; i <= NF; i++) \ if ($i ~ /define/) \ break; \ ++i; \ printf "\tif (!((arg > 0) ^ ((%s) > 0)))\n\t\tif_print_or(arg, %s, or);\n", $i, $i }' cat <<_EOF_ printf(">"); if (or == 0) printf("%jd", arg); } _EOF_ egrep '#define[[:space:]]+CAP_[A-Z_]+[[:space:]]+CAPRIGHT\([0-9],[[:space:]]+0x[0-9]{16}ULL\)' \ - $include_dir/sys/capability.h | \ + $include_dir/sys/capsicum.h | \ sed -E 's/[ ]+/ /g' | \ awk -F '[ \(,\)]' ' BEGIN { printf "void\n" printf "capname(const cap_rights_t *rightsp)\n" printf "{\n" printf "\tint comma = 0;\n\n" printf "\tprintf(\"<\");\n" } { printf "\tif ((rightsp->cr_rights[%s] & %s) == %s) {\n", $4, $2, $2 printf "\t\tif (comma) printf(\",\"); else comma = 1;\n" printf "\t\tprintf(\"%s\");\n", $2 printf "\t}\n" } END { printf "\tprintf(\">\");\n" printf "}\n" }' Index: stable/10/usr.bin/procstat/procstat_files.c =================================================================== --- stable/10/usr.bin/procstat/procstat_files.c (revision 280249) +++ stable/10/usr.bin/procstat/procstat_files.c (revision 280250) @@ -1,505 +1,505 @@ /*- * Copyright (c) 2007-2011 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include "procstat.h" static const char * protocol_to_string(int domain, int type, int protocol) { switch (domain) { case AF_INET: case AF_INET6: switch (protocol) { case IPPROTO_TCP: return ("TCP"); case IPPROTO_UDP: return ("UDP"); case IPPROTO_ICMP: return ("ICM"); case IPPROTO_RAW: return ("RAW"); case IPPROTO_SCTP: return ("SCT"); case IPPROTO_DIVERT: return ("IPD"); default: return ("IP?"); } case AF_LOCAL: switch (type) { case SOCK_STREAM: return ("UDS"); case SOCK_DGRAM: return ("UDD"); default: return ("UD?"); } default: return ("?"); } } static void addr_to_string(struct sockaddr_storage *ss, char *buffer, int buflen) { char buffer2[INET6_ADDRSTRLEN]; struct sockaddr_in6 *sin6; struct sockaddr_in *sin; struct sockaddr_un *sun; switch (ss->ss_family) { case AF_LOCAL: sun = (struct sockaddr_un *)ss; if (strlen(sun->sun_path) == 0) strlcpy(buffer, "-", buflen); else strlcpy(buffer, sun->sun_path, buflen); break; case AF_INET: sin = (struct sockaddr_in *)ss; snprintf(buffer, buflen, "%s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); break; case AF_INET6: sin6 = (struct sockaddr_in6 *)ss; if (inet_ntop(AF_INET6, &sin6->sin6_addr, buffer2, sizeof(buffer2)) != NULL) snprintf(buffer, buflen, "%s.%d", buffer2, ntohs(sin6->sin6_port)); else strlcpy(buffer, "-", buflen); break; default: strlcpy(buffer, "", buflen); break; } } static void print_address(struct sockaddr_storage *ss) { char addr[PATH_MAX]; addr_to_string(ss, addr, sizeof(addr)); printf("%s", addr); } static struct cap_desc { uint64_t cd_right; const char *cd_desc; } cap_desc[] = { /* General file I/O. */ { CAP_READ, "rd" }, { CAP_WRITE, "wr" }, { CAP_SEEK, "se" }, { CAP_MMAP, "mm" }, { CAP_CREATE, "cr" }, { CAP_FEXECVE, "fe" }, { CAP_FSYNC, "fy" }, { CAP_FTRUNCATE, "ft" }, /* VFS methods. */ { CAP_FCHDIR, "cd" }, { CAP_FCHFLAGS, "cf" }, { CAP_FCHMOD, "cm" }, { CAP_FCHOWN, "cn" }, { CAP_FCNTL, "fc" }, { CAP_FLOCK, "fl" }, { CAP_FPATHCONF, "fp" }, { CAP_FSCK, "fk" }, { CAP_FSTAT, "fs" }, { CAP_FSTATFS, "sf" }, { CAP_FUTIMES, "fu" }, { CAP_LINKAT, "li" }, { CAP_MKDIRAT, "md" }, { CAP_MKFIFOAT, "mf" }, { CAP_MKNODAT, "mn" }, { CAP_RENAMEAT, "rn" }, { CAP_SYMLINKAT, "sl" }, { CAP_UNLINKAT, "un" }, /* Lookups - used to constrain *at() calls. */ { CAP_LOOKUP, "lo" }, /* Extended attributes. */ { CAP_EXTATTR_GET, "eg" }, { CAP_EXTATTR_SET, "es" }, { CAP_EXTATTR_DELETE, "ed" }, { CAP_EXTATTR_LIST, "el" }, /* Access Control Lists. */ { CAP_ACL_GET, "ag" }, { CAP_ACL_SET, "as" }, { CAP_ACL_DELETE, "ad" }, { CAP_ACL_CHECK, "ac" }, /* Socket operations. */ { CAP_ACCEPT, "at" }, { CAP_BIND, "bd" }, { CAP_CONNECT, "co" }, { CAP_GETPEERNAME, "pn" }, { CAP_GETSOCKNAME, "sn" }, { CAP_GETSOCKOPT, "gs" }, { CAP_LISTEN, "ln" }, { CAP_PEELOFF, "pf" }, { CAP_SETSOCKOPT, "ss" }, { CAP_SHUTDOWN, "sh" }, /* Mandatory Access Control. */ { CAP_MAC_GET, "mg" }, { CAP_MAC_SET, "ms" }, /* Methods on semaphores. */ { CAP_SEM_GETVALUE, "sg" }, { CAP_SEM_POST, "sp" }, { CAP_SEM_WAIT, "sw" }, /* Event monitoring and posting. */ { CAP_EVENT, "ev" }, { CAP_KQUEUE_EVENT, "ke" }, { CAP_KQUEUE_CHANGE, "kc" }, /* Strange and powerful rights that should not be given lightly. */ { CAP_IOCTL, "io" }, { CAP_TTYHOOK, "ty" }, /* Process management via process descriptors. */ { CAP_PDGETPID, "pg" }, { CAP_PDWAIT, "pw" }, { CAP_PDKILL, "pk" }, /* * Rights that allow to use bindat(2) and connectat(2) syscalls on a * directory descriptor. */ { CAP_BINDAT, "ba" }, { CAP_CONNECTAT, "ca" }, /* Aliases and defines that combine multiple rights. */ { CAP_PREAD, "prd" }, { CAP_PWRITE, "pwr" }, { CAP_MMAP_R, "mmr" }, { CAP_MMAP_W, "mmw" }, { CAP_MMAP_X, "mmx" }, { CAP_MMAP_RW, "mrw" }, { CAP_MMAP_RX, "mrx" }, { CAP_MMAP_WX, "mwx" }, { CAP_MMAP_RWX, "mma" }, { CAP_RECV, "re" }, { CAP_SEND, "sd" }, { CAP_SOCK_CLIENT, "scl" }, { CAP_SOCK_SERVER, "ssr" }, }; static const u_int cap_desc_count = sizeof(cap_desc) / sizeof(cap_desc[0]); static u_int width_capability(cap_rights_t *rightsp) { u_int count, i, width; count = 0; width = 0; for (i = 0; i < cap_desc_count; i++) { if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) { width += strlen(cap_desc[i].cd_desc); if (count) width++; count++; } } return (width); } static void print_capability(cap_rights_t *rightsp, u_int capwidth) { u_int count, i, width; count = 0; width = 0; for (i = width_capability(rightsp); i < capwidth; i++) { if (i != 0) printf(" "); else printf("-"); } for (i = 0; i < cap_desc_count; i++) { if (cap_rights_is_set(rightsp, cap_desc[i].cd_right)) { printf("%s%s", count ? "," : "", cap_desc[i].cd_desc); width += strlen(cap_desc[i].cd_desc); if (count) width++; count++; } } } void procstat_files(struct procstat *procstat, struct kinfo_proc *kipp) { struct sockstat sock; struct filestat_list *head; struct filestat *fst; const char *str; struct vnstat vn; u_int capwidth, width; int error; /* * To print the header in capability mode, we need to know the width * of the widest capability string. Even if we get no processes * back, we will print the header, so we defer aborting due to a lack * of processes until after the header logic. */ capwidth = 0; head = procstat_getfiles(procstat, kipp, 0); if (head != NULL && Cflag) { STAILQ_FOREACH(fst, head, next) { width = width_capability(&fst->fs_cap_rights); if (width > capwidth) capwidth = width; } if (capwidth < strlen("CAPABILITIES")) capwidth = strlen("CAPABILITIES"); } if (!hflag) { if (Cflag) printf("%5s %-16s %4s %1s %-9s %-*s " "%-3s %-12s\n", "PID", "COMM", "FD", "T", "FLAGS", capwidth, "CAPABILITIES", "PRO", "NAME"); else printf("%5s %-16s %4s %1s %1s %-9s " "%3s %7s %-3s %-12s\n", "PID", "COMM", "FD", "T", "V", "FLAGS", "REF", "OFFSET", "PRO", "NAME"); } if (head == NULL) return; STAILQ_FOREACH(fst, head, next) { printf("%5d ", kipp->ki_pid); printf("%-16s ", kipp->ki_comm); if (fst->fs_uflags & PS_FST_UFLAG_CTTY) printf(" ctty "); else if (fst->fs_uflags & PS_FST_UFLAG_CDIR) printf(" cwd "); else if (fst->fs_uflags & PS_FST_UFLAG_JAIL) printf(" jail "); else if (fst->fs_uflags & PS_FST_UFLAG_RDIR) printf(" root "); else if (fst->fs_uflags & PS_FST_UFLAG_TEXT) printf(" text "); else if (fst->fs_uflags & PS_FST_UFLAG_TRACE) printf("trace "); else printf("%5d ", fst->fs_fd); switch (fst->fs_type) { case PS_FST_TYPE_VNODE: str = "v"; break; case PS_FST_TYPE_SOCKET: str = "s"; break; case PS_FST_TYPE_PIPE: str = "p"; break; case PS_FST_TYPE_FIFO: str = "f"; break; case PS_FST_TYPE_KQUEUE: str = "k"; break; case PS_FST_TYPE_CRYPTO: str = "c"; break; case PS_FST_TYPE_MQUEUE: str = "m"; break; case PS_FST_TYPE_SHM: str = "h"; break; case PS_FST_TYPE_PTS: str = "t"; break; case PS_FST_TYPE_SEM: str = "e"; break; case PS_FST_TYPE_NONE: case PS_FST_TYPE_UNKNOWN: default: str = "?"; break; } printf("%1s ", str); if (!Cflag) { str = "-"; if (fst->fs_type == PS_FST_TYPE_VNODE) { error = procstat_get_vnode_info(procstat, fst, &vn, NULL); switch (vn.vn_type) { case PS_FST_VTYPE_VREG: str = "r"; break; case PS_FST_VTYPE_VDIR: str = "d"; break; case PS_FST_VTYPE_VBLK: str = "b"; break; case PS_FST_VTYPE_VCHR: str = "c"; break; case PS_FST_VTYPE_VLNK: str = "l"; break; case PS_FST_VTYPE_VSOCK: str = "s"; break; case PS_FST_VTYPE_VFIFO: str = "f"; break; case PS_FST_VTYPE_VBAD: str = "x"; break; case PS_FST_VTYPE_VNON: case PS_FST_VTYPE_UNKNOWN: default: str = "?"; break; } } printf("%1s ", str); } printf("%s", fst->fs_fflags & PS_FST_FFLAG_READ ? "r" : "-"); printf("%s", fst->fs_fflags & PS_FST_FFLAG_WRITE ? "w" : "-"); printf("%s", fst->fs_fflags & PS_FST_FFLAG_APPEND ? "a" : "-"); printf("%s", fst->fs_fflags & PS_FST_FFLAG_ASYNC ? "s" : "-"); printf("%s", fst->fs_fflags & PS_FST_FFLAG_SYNC ? "f" : "-"); printf("%s", fst->fs_fflags & PS_FST_FFLAG_NONBLOCK ? "n" : "-"); printf("%s", fst->fs_fflags & PS_FST_FFLAG_DIRECT ? "d" : "-"); printf("%s", fst->fs_fflags & PS_FST_FFLAG_HASLOCK ? "l" : "-"); if (!Cflag) { if (fst->fs_ref_count > -1) printf("%3d ", fst->fs_ref_count); else printf("%3c ", '-'); if (fst->fs_offset > -1) printf("%7jd ", (intmax_t)fst->fs_offset); else printf("%7c ", '-'); } if (Cflag) { print_capability(&fst->fs_cap_rights, capwidth); printf(" "); } switch (fst->fs_type) { case PS_FST_TYPE_SOCKET: error = procstat_get_socket_info(procstat, fst, &sock, NULL); if (error != 0) break; printf("%-3s ", protocol_to_string(sock.dom_family, sock.type, sock.proto)); /* * While generally we like to print two addresses, * local and peer, for sockets, it turns out to be * more useful to print the first non-nul address for * local sockets, as typically they aren't bound and * connected, and the path strings can get long. */ if (sock.dom_family == AF_LOCAL) { struct sockaddr_un *sun = (struct sockaddr_un *)&sock.sa_local; if (sun->sun_path[0] != 0) print_address(&sock.sa_local); else print_address(&sock.sa_peer); } else { print_address(&sock.sa_local); printf(" "); print_address(&sock.sa_peer); } break; default: printf("%-3s ", "-"); printf("%-18s", fst->fs_path != NULL ? fst->fs_path : "-"); } printf("\n"); } procstat_freefiles(procstat, head); } Index: stable/10/usr.bin/rwho/rwho.c =================================================================== --- stable/10/usr.bin/rwho/rwho.c (revision 280249) +++ stable/10/usr.bin/rwho/rwho.c (revision 280250) @@ -1,247 +1,247 @@ /*- * Copyright (c) 1983, 1993 The Regents of the University of California. * Copyright (c) 2013 Mariusz Zaborski * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)rwho.c 8.1 (Berkeley) 6/6/93"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUSERS 1000 #define WHDRSIZE (ssize_t)(sizeof(wd) - sizeof(wd.wd_we)) /* * this macro should be shared with ruptime. */ #define down(w,now) ((now) - (w)->wd_recvtime > 11 * 60) static DIR *dirp; static struct whod wd; static int nusers; static struct myutmp { char myhost[sizeof(wd.wd_hostname)]; int myidle; struct outmp myutmp; } myutmp[NUSERS]; static time_t now; static int aflg; static void usage(void); static int utmpcmp(const void *, const void *); int main(int argc, char *argv[]) { int ch; struct dirent *dp; int width; ssize_t cc; struct whod *w; struct whoent *we; struct myutmp *mp; cap_rights_t rights; int f, n, i; int d_first; int dfd; time_t ct; w = &wd; (void) setlocale(LC_TIME, ""); d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); while ((ch = getopt(argc, argv, "a")) != -1) { switch ((char)ch) { case 'a': aflg = 1; break; case '?': default: usage(); } } argc -= optind; argv += optind; if (argc != 0) usage(); if (chdir(_PATH_RWHODIR) < 0) err(1, "chdir(%s)", _PATH_RWHODIR); if ((dirp = opendir(".")) == NULL) err(1, "opendir(%s)", _PATH_RWHODIR); dfd = dirfd(dirp); mp = myutmp; cap_rights_init(&rights, CAP_READ, CAP_LOOKUP); if (cap_rights_limit(dfd, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit failed: %s", _PATH_RWHODIR); /* * Cache files required for time(3) and localtime(3) before entering * capability mode. */ (void) time(&ct); (void) localtime(&ct); if (cap_enter() < 0 && errno != ENOSYS) err(1, "cap_enter"); (void) time(&now); cap_rights_init(&rights, CAP_READ); while ((dp = readdir(dirp)) != NULL) { if (dp->d_ino == 0 || strncmp(dp->d_name, "whod.", 5) != 0) continue; f = openat(dfd, dp->d_name, O_RDONLY); if (f < 0) continue; if (cap_rights_limit(f, &rights) < 0 && errno != ENOSYS) err(1, "cap_rights_limit failed: %s", dp->d_name); cc = read(f, (char *)&wd, sizeof(struct whod)); if (cc < WHDRSIZE) { (void) close(f); continue; } if (down(w, now) != 0) { (void) close(f); continue; } cc -= WHDRSIZE; we = w->wd_we; for (n = cc / sizeof(struct whoent); n > 0; n--) { if (aflg == 0 && we->we_idle >= 60 * 60) { we++; continue; } if (nusers >= NUSERS) errx(1, "too many users"); mp->myutmp = we->we_utmp; mp->myidle = we->we_idle; (void) strcpy(mp->myhost, w->wd_hostname); nusers++; we++; mp++; } (void) close(f); } qsort((char *)myutmp, nusers, sizeof(struct myutmp), utmpcmp); mp = myutmp; width = 0; for (i = 0; i < nusers; i++) { /* append one for the blank and use 8 for the out_line */ int j; j = strlen(mp->myhost) + 1 + sizeof(mp->myutmp.out_line); if (j > width) width = j; mp++; } mp = myutmp; for (i = 0; i < nusers; i++) { char buf[BUFSIZ], cbuf[80]; time_t t; t = _int_to_time(mp->myutmp.out_time); strftime(cbuf, sizeof(cbuf), d_first ? "%e %b %R" : "%b %e %R", localtime(&t)); (void) sprintf(buf, "%s:%-.*s", mp->myhost, (int)sizeof(mp->myutmp.out_line), mp->myutmp.out_line); printf("%-*.*s %-*s %s", (int)sizeof(mp->myutmp.out_name), (int)sizeof(mp->myutmp.out_name), mp->myutmp.out_name, width, buf, cbuf); mp->myidle /= 60; if (mp->myidle != 0) { if (aflg != 0) { if (mp->myidle >= 100 * 60) mp->myidle = 100 * 60 - 1; if (mp->myidle >= 60) printf(" %2d", mp->myidle / 60); else printf(" "); } else { printf(" "); } printf(":%02d", mp->myidle % 60); } printf("\n"); mp++; } exit(0); } static void usage(void) { fprintf(stderr, "usage: rwho [-a]\n"); exit(1); } #define MYUTMP(a) ((const struct myutmp *)(a)) static int utmpcmp(const void *u1, const void *u2) { int rc; rc = strncmp(MYUTMP(u1)->myutmp.out_name, MYUTMP(u2)->myutmp.out_name, sizeof(MYUTMP(u2)->myutmp.out_name)); if (rc != 0) return (rc); rc = strcmp(MYUTMP(u1)->myhost, MYUTMP(u2)->myhost); if (rc != 0) return (rc); return (strncmp(MYUTMP(u1)->myutmp.out_line, MYUTMP(u2)->myutmp.out_line, sizeof(MYUTMP(u2)->myutmp.out_line))); } Index: stable/10/usr.bin/uniq/uniq.c =================================================================== --- stable/10/usr.bin/uniq/uniq.c (revision 280249) +++ stable/10/usr.bin/uniq/uniq.c (revision 280250) @@ -1,357 +1,357 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Case Larsen. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)uniq.c 8.3 (Berkeley) 5/4/95"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ -#include +#include #include #include #include #include #include #include #include #define _WITH_GETLINE #include #include #include #include #include #include #include static int cflag, dflag, uflag, iflag; static int numchars, numfields, repeats; static FILE *file(const char *, const char *); static wchar_t *convert(const char *); static int inlcmp(const char *, const char *); static void show(FILE *, const char *); static wchar_t *skip(wchar_t *); static void obsolete(char *[]); static void usage(void); static void strerror_init(void) { /* * Cache NLS data before entering capability mode. * XXXPJD: There should be strerror_init() and strsignal_init() in libc. */ (void)catopen("libc", NL_CAT_LOCALE); } int main (int argc, char *argv[]) { wchar_t *tprev, *tthis; FILE *ifp, *ofp; int ch, comp; size_t prevbuflen, thisbuflen, b1; char *prevline, *thisline, *p; const char *ifn; cap_rights_t rights; (void) setlocale(LC_ALL, ""); obsolete(argv); while ((ch = getopt(argc, argv, "cdif:s:u")) != -1) switch (ch) { case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'i': iflag = 1; break; case 'f': numfields = strtol(optarg, &p, 10); if (numfields < 0 || *p) errx(1, "illegal field skip value: %s", optarg); break; case 's': numchars = strtol(optarg, &p, 10); if (numchars < 0 || *p) errx(1, "illegal character skip value: %s", optarg); break; case 'u': uflag = 1; break; case '?': default: usage(); } argc -= optind; argv += optind; /* If no flags are set, default is -d -u. */ if (cflag) { if (dflag || uflag) usage(); } else if (!dflag && !uflag) dflag = uflag = 1; if (argc > 2) usage(); ifp = stdin; ifn = "stdin"; ofp = stdout; if (argc > 0 && strcmp(argv[0], "-") != 0) ifp = file(ifn = argv[0], "r"); cap_rights_init(&rights, CAP_FSTAT, CAP_READ); if (cap_rights_limit(fileno(ifp), &rights) < 0 && errno != ENOSYS) err(1, "unable to limit rights for %s", ifn); cap_rights_init(&rights, CAP_FSTAT, CAP_WRITE); if (argc > 1) ofp = file(argv[1], "w"); else cap_rights_set(&rights, CAP_IOCTL); if (cap_rights_limit(fileno(ofp), &rights) < 0 && errno != ENOSYS) { err(1, "unable to limit rights for %s", argc > 1 ? argv[1] : "stdout"); } if (cap_rights_is_set(&rights, CAP_IOCTL)) { unsigned long cmd; cmd = TIOCGETA; /* required by isatty(3) in printf(3) */ if (cap_ioctls_limit(fileno(ofp), &cmd, 1) < 0 && errno != ENOSYS) { err(1, "unable to limit ioctls for %s", argc > 1 ? argv[1] : "stdout"); } } strerror_init(); if (cap_enter() < 0 && errno != ENOSYS) err(1, "unable to enter capability mode"); prevbuflen = thisbuflen = 0; prevline = thisline = NULL; if (getline(&prevline, &prevbuflen, ifp) < 0) { if (ferror(ifp)) err(1, "%s", ifn); exit(0); } tprev = convert(prevline); if (!cflag && uflag && dflag) show(ofp, prevline); tthis = NULL; while (getline(&thisline, &thisbuflen, ifp) >= 0) { if (tthis != NULL) free(tthis); tthis = convert(thisline); if (tthis == NULL && tprev == NULL) comp = inlcmp(thisline, prevline); else if (tthis == NULL || tprev == NULL) comp = 1; else comp = wcscoll(tthis, tprev); if (comp) { /* If different, print; set previous to new value. */ if (cflag || !dflag || !uflag) show(ofp, prevline); p = prevline; b1 = prevbuflen; prevline = thisline; prevbuflen = thisbuflen; if (tprev != NULL) free(tprev); tprev = tthis; if (!cflag && uflag && dflag) show(ofp, prevline); thisline = p; thisbuflen = b1; tthis = NULL; repeats = 0; } else ++repeats; } if (ferror(ifp)) err(1, "%s", ifn); if (cflag || !dflag || !uflag) show(ofp, prevline); exit(0); } static wchar_t * convert(const char *str) { size_t n; wchar_t *buf, *ret, *p; if ((n = mbstowcs(NULL, str, 0)) == (size_t)-1) return (NULL); if (SIZE_MAX / sizeof(*buf) < n + 1) errx(1, "conversion buffer length overflow"); if ((buf = malloc((n + 1) * sizeof(*buf))) == NULL) err(1, "malloc"); if (mbstowcs(buf, str, n + 1) != n) errx(1, "internal mbstowcs() error"); /* The last line may not end with \n. */ if (n > 0 && buf[n - 1] == L'\n') buf[n - 1] = L'\0'; /* If requested get the chosen fields + character offsets. */ if (numfields || numchars) { if ((ret = wcsdup(skip(buf))) == NULL) err(1, "wcsdup"); free(buf); } else ret = buf; if (iflag) { for (p = ret; *p != L'\0'; p++) *p = towlower(*p); } return (ret); } static int inlcmp(const char *s1, const char *s2) { int c1, c2; while (*s1 == *s2++) if (*s1++ == '\0') return (0); c1 = (unsigned char)*s1; c2 = (unsigned char)*(s2 - 1); /* The last line may not end with \n. */ if (c1 == '\n') c1 = '\0'; if (c2 == '\n') c2 = '\0'; return (c1 - c2); } /* * show -- * Output a line depending on the flags and number of repetitions * of the line. */ static void show(FILE *ofp, const char *str) { if (cflag) (void)fprintf(ofp, "%4d %s", repeats + 1, str); if ((dflag && repeats) || (uflag && !repeats)) (void)fprintf(ofp, "%s", str); } static wchar_t * skip(wchar_t *str) { int nchars, nfields; for (nfields = 0; *str != L'\0' && nfields++ != numfields; ) { while (iswblank(*str)) str++; while (*str != L'\0' && !iswblank(*str)) str++; } for (nchars = numchars; nchars-- && *str != L'\0'; ++str) ; return(str); } static FILE * file(const char *name, const char *mode) { FILE *fp; if ((fp = fopen(name, mode)) == NULL) err(1, "%s", name); return(fp); } static void obsolete(char *argv[]) { int len; char *ap, *p, *start; while ((ap = *++argv)) { /* Return if "--" or not an option of any form. */ if (ap[0] != '-') { if (ap[0] != '+') return; } else if (ap[1] == '-') return; if (!isdigit((unsigned char)ap[1])) continue; /* * Digit signifies an old-style option. Malloc space for dash, * new option and argument. */ len = strlen(ap); if ((start = p = malloc(len + 3)) == NULL) err(1, "malloc"); *p++ = '-'; *p++ = ap[0] == '+' ? 's' : 'f'; (void)strcpy(p, ap + 1); *argv = start; } } static void usage(void) { (void)fprintf(stderr, "usage: uniq [-c | -d | -u] [-i] [-f fields] [-s chars] [input [output]]\n"); exit(1); } Index: stable/10/usr.sbin/ctld/kernel.c =================================================================== --- stable/10/usr.sbin/ctld/kernel.c (revision 280249) +++ stable/10/usr.sbin/ctld/kernel.c (revision 280250) @@ -1,1165 +1,1165 @@ /*- * Copyright (c) 2003, 2004 Silicon Graphics International Corp. * Copyright (c) 1997-2007 Kenneth D. Merry * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ctld.h" #ifdef ICL_KERNEL_PROXY #include #endif extern bool proxy_mode; static int ctl_fd = 0; void kernel_init(void) { int retval, saved_errno; ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); if (ctl_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("ctl"); if (retval != -1) ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); else errno = saved_errno; } if (ctl_fd < 0) log_err(1, "failed to open %s", CTL_DEFAULT_DEV); } /* * Name/value pair used for per-LUN attributes. */ struct cctl_lun_nv { char *name; char *value; STAILQ_ENTRY(cctl_lun_nv) links; }; /* * Backend LUN information. */ struct cctl_lun { uint64_t lun_id; char *backend_type; uint64_t size_blocks; uint32_t blocksize; char *serial_number; char *device_id; char *ctld_name; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_lun) links; }; struct cctl_port { uint32_t port_id; char *port_name; int cfiscsi_state; char *cfiscsi_target; uint16_t cfiscsi_portal_group_tag; char *ctld_portal_group_name; STAILQ_HEAD(,cctl_lun_nv) attr_list; STAILQ_ENTRY(cctl_port) links; }; struct cctl_devlist_data { int num_luns; STAILQ_HEAD(,cctl_lun) lun_list; struct cctl_lun *cur_lun; int num_ports; STAILQ_HEAD(,cctl_port) port_list; struct cctl_port *cur_port; int level; struct sbuf *cur_sb[32]; }; static void cctl_start_element(void *user_data, const char *name, const char **attr) { int i; struct cctl_devlist_data *devlist; struct cctl_lun *cur_lun; devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; devlist->level++; if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) log_errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); devlist->cur_sb[devlist->level] = sbuf_new_auto(); if (devlist->cur_sb[devlist->level] == NULL) log_err(1, "%s: unable to allocate sbuf", __func__); if (strcmp(name, "lun") == 0) { if (cur_lun != NULL) log_errx(1, "%s: improper lun element nesting", __func__); cur_lun = calloc(1, sizeof(*cur_lun)); if (cur_lun == NULL) log_err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_lun)); devlist->num_luns++; devlist->cur_lun = cur_lun; STAILQ_INIT(&cur_lun->attr_list); STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links); for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { cur_lun->lun_id = strtoull(attr[i+1], NULL, 0); } else { log_errx(1, "%s: invalid LUN attribute %s = %s", __func__, attr[i], attr[i+1]); } } } } static void cctl_end_element(void *user_data, const char *name) { struct cctl_devlist_data *devlist; struct cctl_lun *cur_lun; char *str; devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; if ((cur_lun == NULL) && (strcmp(name, "ctllunlist") != 0)) log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name); if (devlist->cur_sb[devlist->level] == NULL) log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, devlist->level, name); sbuf_finish(devlist->cur_sb[devlist->level]); str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level])); if (strlen(str) == 0) { free(str); str = NULL; } sbuf_delete(devlist->cur_sb[devlist->level]); devlist->cur_sb[devlist->level] = NULL; devlist->level--; if (strcmp(name, "backend_type") == 0) { cur_lun->backend_type = str; str = NULL; } else if (strcmp(name, "size") == 0) { cur_lun->size_blocks = strtoull(str, NULL, 0); } else if (strcmp(name, "blocksize") == 0) { cur_lun->blocksize = strtoul(str, NULL, 0); } else if (strcmp(name, "serial_number") == 0) { cur_lun->serial_number = str; str = NULL; } else if (strcmp(name, "device_id") == 0) { cur_lun->device_id = str; str = NULL; } else if (strcmp(name, "ctld_name") == 0) { cur_lun->ctld_name = str; str = NULL; } else if (strcmp(name, "lun") == 0) { devlist->cur_lun = NULL; } else if (strcmp(name, "ctllunlist") == 0) { /* Nothing. */ } else { struct cctl_lun_nv *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) log_err(1, "%s: can't allocate %zd bytes for nv pair", __func__, sizeof(*nv)); nv->name = checked_strdup(name); nv->value = str; str = NULL; STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links); } free(str); } static void cctl_start_pelement(void *user_data, const char *name, const char **attr) { int i; struct cctl_devlist_data *devlist; struct cctl_port *cur_port; devlist = (struct cctl_devlist_data *)user_data; cur_port = devlist->cur_port; devlist->level++; if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) log_errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); devlist->cur_sb[devlist->level] = sbuf_new_auto(); if (devlist->cur_sb[devlist->level] == NULL) log_err(1, "%s: unable to allocate sbuf", __func__); if (strcmp(name, "targ_port") == 0) { if (cur_port != NULL) log_errx(1, "%s: improper port element nesting (%s)", __func__, name); cur_port = calloc(1, sizeof(*cur_port)); if (cur_port == NULL) log_err(1, "%s: cannot allocate %zd bytes", __func__, sizeof(*cur_port)); devlist->num_ports++; devlist->cur_port = cur_port; STAILQ_INIT(&cur_port->attr_list); STAILQ_INSERT_TAIL(&devlist->port_list, cur_port, links); for (i = 0; attr[i] != NULL; i += 2) { if (strcmp(attr[i], "id") == 0) { cur_port->port_id = strtoul(attr[i+1], NULL, 0); } else { log_errx(1, "%s: invalid LUN attribute %s = %s", __func__, attr[i], attr[i+1]); } } } } static void cctl_end_pelement(void *user_data, const char *name) { struct cctl_devlist_data *devlist; struct cctl_port *cur_port; char *str; devlist = (struct cctl_devlist_data *)user_data; cur_port = devlist->cur_port; if ((cur_port == NULL) && (strcmp(name, "ctlportlist") != 0)) log_errx(1, "%s: cur_port == NULL! (name = %s)", __func__, name); if (devlist->cur_sb[devlist->level] == NULL) log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, devlist->level, name); sbuf_finish(devlist->cur_sb[devlist->level]); str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level])); if (strlen(str) == 0) { free(str); str = NULL; } sbuf_delete(devlist->cur_sb[devlist->level]); devlist->cur_sb[devlist->level] = NULL; devlist->level--; if (strcmp(name, "port_name") == 0) { cur_port->port_name = str; str = NULL; } else if (strcmp(name, "cfiscsi_target") == 0) { cur_port->cfiscsi_target = str; str = NULL; } else if (strcmp(name, "cfiscsi_state") == 0) { cur_port->cfiscsi_state = strtoul(str, NULL, 0); } else if (strcmp(name, "cfiscsi_portal_group_tag") == 0) { cur_port->cfiscsi_portal_group_tag = strtoul(str, NULL, 0); } else if (strcmp(name, "ctld_portal_group_name") == 0) { cur_port->ctld_portal_group_name = str; str = NULL; } else if (strcmp(name, "targ_port") == 0) { devlist->cur_port = NULL; } else if (strcmp(name, "ctlportlist") == 0) { /* Nothing. */ } else { struct cctl_lun_nv *nv; nv = calloc(1, sizeof(*nv)); if (nv == NULL) log_err(1, "%s: can't allocate %zd bytes for nv pair", __func__, sizeof(*nv)); nv->name = checked_strdup(name); nv->value = str; str = NULL; STAILQ_INSERT_TAIL(&cur_port->attr_list, nv, links); } free(str); } static void cctl_char_handler(void *user_data, const XML_Char *str, int len) { struct cctl_devlist_data *devlist; devlist = (struct cctl_devlist_data *)user_data; sbuf_bcat(devlist->cur_sb[devlist->level], str, len); } struct conf * conf_new_from_kernel(void) { struct conf *conf = NULL; struct target *targ; struct portal_group *pg; struct pport *pp; struct port *cp; struct lun *cl; struct lun_option *lo; struct ctl_lun_list list; struct cctl_devlist_data devlist; struct cctl_lun *lun; struct cctl_port *port; XML_Parser parser; char *str; int len, retval; bzero(&devlist, sizeof(devlist)); STAILQ_INIT(&devlist.lun_list); STAILQ_INIT(&devlist.port_list); log_debugx("obtaining previously configured CTL luns from the kernel"); str = NULL; len = 4096; retry: str = realloc(str, len); if (str == NULL) log_err(1, "realloc"); bzero(&list, sizeof(list)); list.alloc_len = len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = str; if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) { log_warn("error issuing CTL_LUN_LIST ioctl"); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_ERROR) { log_warnx("error returned from CTL_LUN_LIST ioctl: %s", list.error_str); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { len = len << 1; goto retry; } parser = XML_ParserCreate(NULL); if (parser == NULL) { log_warnx("unable to create XML parser"); free(str); return (NULL); } XML_SetUserData(parser, &devlist); XML_SetElementHandler(parser, cctl_start_element, cctl_end_element); XML_SetCharacterDataHandler(parser, cctl_char_handler); retval = XML_Parse(parser, str, strlen(str), 1); XML_ParserFree(parser); free(str); if (retval != 1) { log_warnx("XML_Parse failed"); return (NULL); } str = NULL; len = 4096; retry_port: str = realloc(str, len); if (str == NULL) log_err(1, "realloc"); bzero(&list, sizeof(list)); list.alloc_len = len; list.status = CTL_LUN_LIST_NONE; list.lun_xml = str; if (ioctl(ctl_fd, CTL_PORT_LIST, &list) == -1) { log_warn("error issuing CTL_PORT_LIST ioctl"); free(str); return (NULL); } if (list.status == CTL_PORT_LIST_ERROR) { log_warnx("error returned from CTL_PORT_LIST ioctl: %s", list.error_str); free(str); return (NULL); } if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { len = len << 1; goto retry_port; } parser = XML_ParserCreate(NULL); if (parser == NULL) { log_warnx("unable to create XML parser"); free(str); return (NULL); } XML_SetUserData(parser, &devlist); XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement); XML_SetCharacterDataHandler(parser, cctl_char_handler); retval = XML_Parse(parser, str, strlen(str), 1); XML_ParserFree(parser); free(str); if (retval != 1) { log_warnx("XML_Parse failed"); return (NULL); } conf = conf_new(); STAILQ_FOREACH(port, &devlist.port_list, links) { if (port->cfiscsi_target == NULL) { log_debugx("CTL port %u \"%s\" wasn't managed by ctld; ", port->port_id, port->port_name); pp = pport_find(conf, port->port_name); if (pp == NULL) { #if 0 log_debugx("found new kernel port %u \"%s\"", port->port_id, port->port_name); #endif pp = pport_new(conf, port->port_name, port->port_id); if (pp == NULL) { log_warnx("pport_new failed"); continue; } } continue; } if (port->cfiscsi_state != 1) { log_debugx("CTL port %ju is not active (%d); ignoring", (uintmax_t)port->port_id, port->cfiscsi_state); continue; } targ = target_find(conf, port->cfiscsi_target); if (targ == NULL) { #if 0 log_debugx("found new kernel target %s for CTL port %ld", port->cfiscsi_target, port->port_id); #endif targ = target_new(conf, port->cfiscsi_target); if (targ == NULL) { log_warnx("target_new failed"); continue; } } if (port->ctld_portal_group_name == NULL) continue; pg = portal_group_find(conf, port->ctld_portal_group_name); if (pg == NULL) { #if 0 log_debugx("found new kernel portal group %s for CTL port %ld", port->ctld_portal_group_name, port->port_id); #endif pg = portal_group_new(conf, port->ctld_portal_group_name); if (pg == NULL) { log_warnx("portal_group_new failed"); continue; } } pg->pg_tag = port->cfiscsi_portal_group_tag; cp = port_new(conf, targ, pg); if (cp == NULL) { log_warnx("port_new failed"); continue; } cp->p_ctl_port = port->port_id; } STAILQ_FOREACH(lun, &devlist.lun_list, links) { struct cctl_lun_nv *nv; if (lun->ctld_name == NULL) { log_debugx("CTL lun %ju wasn't managed by ctld; " "ignoring", (uintmax_t)lun->lun_id); continue; } cl = lun_find(conf, lun->ctld_name); if (cl != NULL) { log_warnx("found CTL lun %ju \"%s\", " "also backed by CTL lun %d; ignoring", (uintmax_t)lun->lun_id, lun->ctld_name, cl->l_ctl_lun); continue; } log_debugx("found CTL lun %ju \"%s\"", (uintmax_t)lun->lun_id, lun->ctld_name); cl = lun_new(conf, lun->ctld_name); if (cl == NULL) { log_warnx("lun_new failed"); continue; } lun_set_backend(cl, lun->backend_type); lun_set_blocksize(cl, lun->blocksize); lun_set_device_id(cl, lun->device_id); lun_set_serial(cl, lun->serial_number); lun_set_size(cl, lun->size_blocks * cl->l_blocksize); lun_set_ctl_lun(cl, lun->lun_id); STAILQ_FOREACH(nv, &lun->attr_list, links) { if (strcmp(nv->name, "file") == 0 || strcmp(nv->name, "dev") == 0) { lun_set_path(cl, nv->value); continue; } lo = lun_option_new(cl, nv->name, nv->value); if (lo == NULL) log_warnx("unable to add CTL lun option %s " "for CTL lun %ju \"%s\"", nv->name, (uintmax_t) lun->lun_id, cl->l_name); } } return (conf); } static void str_arg(struct ctl_be_arg *arg, const char *name, const char *value) { arg->namelen = strlen(name) + 1; arg->name = __DECONST(char *, name); arg->vallen = strlen(value) + 1; arg->value = __DECONST(char *, value); arg->flags = CTL_BEARG_ASCII | CTL_BEARG_RD; } int kernel_lun_add(struct lun *lun) { struct lun_option *lo; struct ctl_lun_req req; int error, i, num_options; bzero(&req, sizeof(req)); strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_CREATE; req.reqdata.create.blocksize_bytes = lun->l_blocksize; if (lun->l_size != 0) req.reqdata.create.lun_size_bytes = lun->l_size; req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE; req.reqdata.create.device_type = T_DIRECT; if (lun->l_serial != NULL) { strncpy(req.reqdata.create.serial_num, lun->l_serial, sizeof(req.reqdata.create.serial_num)); req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM; } if (lun->l_device_id != NULL) { strncpy(req.reqdata.create.device_id, lun->l_device_id, sizeof(req.reqdata.create.device_id)); req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID; } if (lun->l_path != NULL) { lo = lun_option_find(lun, "file"); if (lo != NULL) { lun_option_set(lo, lun->l_path); } else { lo = lun_option_new(lun, "file", lun->l_path); assert(lo != NULL); } } lo = lun_option_find(lun, "ctld_name"); if (lo != NULL) { lun_option_set(lo, lun->l_name); } else { lo = lun_option_new(lun, "ctld_name", lun->l_name); assert(lo != NULL); } lo = lun_option_find(lun, "scsiname"); if (lo == NULL && lun->l_scsiname != NULL) { lo = lun_option_new(lun, "scsiname", lun->l_scsiname); assert(lo != NULL); } num_options = 0; TAILQ_FOREACH(lo, &lun->l_options, lo_next) num_options++; req.num_be_args = num_options; if (num_options > 0) { req.be_args = malloc(num_options * sizeof(*req.be_args)); if (req.be_args == NULL) { log_warn("error allocating %zd bytes", num_options * sizeof(*req.be_args)); return (1); } i = 0; TAILQ_FOREACH(lo, &lun->l_options, lo_next) { str_arg(&req.be_args[i], lo->lo_name, lo->lo_value); i++; } assert(i == num_options); } error = ioctl(ctl_fd, CTL_LUN_REQ, &req); free(req.be_args); if (error != 0) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN creation error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN creation warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN creation status: %d", req.status); return (1); } lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id); return (0); } int kernel_lun_resize(struct lun *lun) { struct ctl_lun_req req; bzero(&req, sizeof(req)); strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_MODIFY; req.reqdata.modify.lun_id = lun->l_ctl_lun; req.reqdata.modify.lun_size_bytes = lun->l_size; if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN modification error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN modification warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN modification status: %d", req.status); return (1); } return (0); } int kernel_lun_remove(struct lun *lun) { struct ctl_lun_req req; bzero(&req, sizeof(req)); strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); req.reqtype = CTL_LUNREQ_RM; req.reqdata.rm.lun_id = lun->l_ctl_lun; if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { log_warn("error issuing CTL_LUN_REQ ioctl"); return (1); } switch (req.status) { case CTL_LUN_ERROR: log_warnx("LUN removal error: %s", req.error_str); return (1); case CTL_LUN_WARNING: log_warnx("LUN removal warning: %s", req.error_str); break; case CTL_LUN_OK: break; default: log_warnx("unknown LUN removal status: %d", req.status); return (1); } return (0); } void kernel_handoff(struct connection *conn) { struct ctl_iscsi req; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_HANDOFF; strlcpy(req.data.handoff.initiator_name, conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name)); strlcpy(req.data.handoff.initiator_addr, conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr)); if (conn->conn_initiator_alias != NULL) { strlcpy(req.data.handoff.initiator_alias, conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias)); } memcpy(req.data.handoff.initiator_isid, conn->conn_initiator_isid, sizeof(req.data.handoff.initiator_isid)); strlcpy(req.data.handoff.target_name, conn->conn_target->t_name, sizeof(req.data.handoff.target_name)); #ifdef ICL_KERNEL_PROXY if (proxy_mode) req.data.handoff.connection_id = conn->conn_socket; else req.data.handoff.socket = conn->conn_socket; #else req.data.handoff.socket = conn->conn_socket; #endif req.data.handoff.portal_group_tag = conn->conn_portal->p_portal_group->pg_tag; if (conn->conn_header_digest == CONN_DIGEST_CRC32C) req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C; if (conn->conn_data_digest == CONN_DIGEST_CRC32C) req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C; req.data.handoff.cmdsn = conn->conn_cmdsn; req.data.handoff.statsn = conn->conn_statsn; req.data.handoff.max_recv_data_segment_length = conn->conn_max_data_segment_length; req.data.handoff.max_burst_length = conn->conn_max_burst_length; req.data.handoff.immediate_data = conn->conn_immediate_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { log_err(1, "error issuing CTL_ISCSI ioctl; " "dropping connection"); } if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI handoff request: " "%s; dropping connection", req.error_str); } } int kernel_port_add(struct port *port) { struct ctl_port_entry entry; struct ctl_req req; struct ctl_lun_map lm; struct target *targ = port->p_target; struct portal_group *pg = port->p_portal_group; char tagstr[16]; int error, i, n; /* Create iSCSI port. */ if (port->p_portal_group) { bzero(&req, sizeof(req)); strlcpy(req.driver, "iscsi", sizeof(req.driver)); req.reqtype = CTL_REQ_CREATE; req.num_args = 5; req.args = malloc(req.num_args * sizeof(*req.args)); if (req.args == NULL) log_err(1, "malloc"); n = 0; req.args[n].namelen = sizeof("port_id"); req.args[n].name = __DECONST(char *, "port_id"); req.args[n].vallen = sizeof(port->p_ctl_port); req.args[n].value = &port->p_ctl_port; req.args[n++].flags = CTL_BEARG_WR; str_arg(&req.args[n++], "cfiscsi_target", targ->t_name); snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag); str_arg(&req.args[n++], "cfiscsi_portal_group_tag", tagstr); if (targ->t_alias) str_arg(&req.args[n++], "cfiscsi_target_alias", targ->t_alias); str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name); req.num_args = n; error = ioctl(ctl_fd, CTL_PORT_REQ, &req); free(req.args); if (error != 0) { log_warn("error issuing CTL_PORT_REQ ioctl"); return (1); } if (req.status == CTL_LUN_ERROR) { log_warnx("error returned from port creation request: %s", req.error_str); return (1); } if (req.status != CTL_LUN_OK) { log_warnx("unknown port creation request status %d", req.status); return (1); } } else if (port->p_pport) { port->p_ctl_port = port->p_pport->pp_ctl_port; if (strncmp(targ->t_name, "naa.", 4) == 0 && strlen(targ->t_name) == 20) { bzero(&entry, sizeof(entry)); entry.port_type = CTL_PORT_NONE; entry.targ_port = port->p_ctl_port; entry.flags |= CTL_PORT_WWNN_VALID; entry.wwnn = strtoull(targ->t_name + 4, NULL, 16); if (ioctl(ctl_fd, CTL_SET_PORT_WWNS, &entry) == -1) log_warn("CTL_SET_PORT_WWNS ioctl failed"); } } /* Explicitly enable mapping to block any access except allowed. */ lm.port = port->p_ctl_port; lm.plun = UINT32_MAX; lm.lun = 0; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); /* Map configured LUNs */ for (i = 0; i < MAX_LUNS; i++) { if (targ->t_luns[i] == NULL) continue; lm.port = port->p_ctl_port; lm.plun = i; lm.lun = targ->t_luns[i]->l_ctl_lun; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } /* Enable port */ bzero(&entry, sizeof(entry)); entry.targ_port = port->p_ctl_port; error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry); if (error != 0) { log_warn("CTL_ENABLE_PORT ioctl failed"); return (-1); } return (0); } int kernel_port_update(struct port *port) { struct ctl_lun_map lm; struct target *targ = port->p_target; int error, i; /* Map configured LUNs and unmap others */ for (i = 0; i < MAX_LUNS; i++) { lm.port = port->p_ctl_port; lm.plun = i; if (targ->t_luns[i] == NULL) lm.lun = UINT32_MAX; else lm.lun = targ->t_luns[i]->l_ctl_lun; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } return (0); } int kernel_port_remove(struct port *port) { struct ctl_port_entry entry; struct ctl_lun_map lm; struct ctl_req req; char tagstr[16]; struct target *targ = port->p_target; struct portal_group *pg = port->p_portal_group; int error; /* Disable port */ bzero(&entry, sizeof(entry)); entry.targ_port = port->p_ctl_port; error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry); if (error != 0) { log_warn("CTL_DISABLE_PORT ioctl failed"); return (-1); } /* Remove iSCSI port. */ if (port->p_portal_group) { bzero(&req, sizeof(req)); strlcpy(req.driver, "iscsi", sizeof(req.driver)); req.reqtype = CTL_REQ_REMOVE; req.num_args = 2; req.args = malloc(req.num_args * sizeof(*req.args)); if (req.args == NULL) log_err(1, "malloc"); str_arg(&req.args[0], "cfiscsi_target", targ->t_name); snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag); str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr); error = ioctl(ctl_fd, CTL_PORT_REQ, &req); free(req.args); if (error != 0) { log_warn("error issuing CTL_PORT_REQ ioctl"); return (1); } if (req.status == CTL_LUN_ERROR) { log_warnx("error returned from port removal request: %s", req.error_str); return (1); } if (req.status != CTL_LUN_OK) { log_warnx("unknown port removal request status %d", req.status); return (1); } } else { /* Disable LUN mapping. */ lm.port = port->p_ctl_port; lm.plun = UINT32_MAX; lm.lun = UINT32_MAX; error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); if (error != 0) log_warn("CTL_LUN_MAP ioctl failed"); } return (0); } #ifdef ICL_KERNEL_PROXY void kernel_listen(struct addrinfo *ai, bool iser, int portal_id) { struct ctl_iscsi req; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_LISTEN; req.data.listen.iser = iser; req.data.listen.domain = ai->ai_family; req.data.listen.socktype = ai->ai_socktype; req.data.listen.protocol = ai->ai_protocol; req.data.listen.addr = ai->ai_addr; req.data.listen.addrlen = ai->ai_addrlen; req.data.listen.portal_id = portal_id; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) log_err(1, "error issuing CTL_ISCSI ioctl"); if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI listen: %s", req.error_str); } } void kernel_accept(int *connection_id, int *portal_id, struct sockaddr *client_sa, socklen_t *client_salen) { struct ctl_iscsi req; struct sockaddr_storage ss; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_ACCEPT; req.data.accept.initiator_addr = (struct sockaddr *)&ss; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) log_err(1, "error issuing CTL_ISCSI ioctl"); if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI accept: %s", req.error_str); } *connection_id = req.data.accept.connection_id; *portal_id = req.data.accept.portal_id; *client_salen = req.data.accept.initiator_addrlen; memcpy(client_sa, &ss, *client_salen); } void kernel_send(struct pdu *pdu) { struct ctl_iscsi req; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_SEND; req.data.send.connection_id = pdu->pdu_connection->conn_socket; req.data.send.bhs = pdu->pdu_bhs; req.data.send.data_segment_len = pdu->pdu_data_len; req.data.send.data_segment = pdu->pdu_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { log_err(1, "error issuing CTL_ISCSI ioctl; " "dropping connection"); } if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI send: " "%s; dropping connection", req.error_str); } } void kernel_receive(struct pdu *pdu) { struct ctl_iscsi req; pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH); if (pdu->pdu_data == NULL) log_err(1, "malloc"); bzero(&req, sizeof(req)); req.type = CTL_ISCSI_RECEIVE; req.data.receive.connection_id = pdu->pdu_connection->conn_socket; req.data.receive.bhs = pdu->pdu_bhs; req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH; req.data.receive.data_segment = pdu->pdu_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { log_err(1, "error issuing CTL_ISCSI ioctl; " "dropping connection"); } if (req.status != CTL_ISCSI_OK) { log_errx(1, "error returned from CTL iSCSI receive: " "%s; dropping connection", req.error_str); } } #endif /* ICL_KERNEL_PROXY */ /* * XXX: I CANT INTO LATIN */ void kernel_capsicate(void) { int error; cap_rights_t rights; const unsigned long cmds[] = { CTL_ISCSI }; cap_rights_init(&rights, CAP_IOCTL); error = cap_rights_limit(ctl_fd, &rights); if (error != 0 && errno != ENOSYS) log_err(1, "cap_rights_limit"); error = cap_ioctls_limit(ctl_fd, cmds, sizeof(cmds) / sizeof(cmds[0])); if (error != 0 && errno != ENOSYS) log_err(1, "cap_ioctls_limit"); error = cap_enter(); if (error != 0 && errno != ENOSYS) log_err(1, "cap_enter"); if (cap_sandboxed()) log_debugx("Capsicum capability mode enabled"); else log_warnx("Capsicum capability mode not supported"); } Index: stable/10/usr.sbin/iscsid/iscsid.c =================================================================== --- stable/10/usr.sbin/iscsid/iscsid.c (revision 280249) +++ stable/10/usr.sbin/iscsid/iscsid.c (revision 280250) @@ -1,608 +1,608 @@ /*- * Copyright (c) 2012 The FreeBSD Foundation * All rights reserved. * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iscsid.h" static volatile bool sigalrm_received = false; static int nchildren = 0; static void usage(void) { fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n"); exit(1); } char * checked_strdup(const char *s) { char *c; c = strdup(s); if (c == NULL) log_err(1, "strdup"); return (c); } static void resolve_addr(const struct connection *conn, const char *address, struct addrinfo **ai, bool initiator_side) { struct addrinfo hints; char *arg, *addr, *ch; const char *port; int error, colons = 0; arg = checked_strdup(address); if (arg[0] == '\0') { fail(conn, "empty address"); log_errx(1, "empty address"); } if (arg[0] == '[') { /* * IPv6 address in square brackets, perhaps with port. */ arg++; addr = strsep(&arg, "]"); if (arg == NULL) { fail(conn, "malformed address"); log_errx(1, "malformed address %s", address); } if (arg[0] == '\0') { port = NULL; } else if (arg[0] == ':') { port = arg + 1; } else { fail(conn, "malformed address"); log_errx(1, "malformed address %s", address); } } else { /* * Either IPv6 address without brackets - and without * a port - or IPv4 address. Just count the colons. */ for (ch = arg; *ch != '\0'; ch++) { if (*ch == ':') colons++; } if (colons > 1) { addr = arg; port = NULL; } else { addr = strsep(&arg, ":"); if (arg == NULL) port = NULL; else port = arg; } } if (port == NULL && !initiator_side) port = "3260"; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; if (initiator_side) hints.ai_flags |= AI_PASSIVE; error = getaddrinfo(addr, port, &hints, ai); if (error != 0) { fail(conn, gai_strerror(error)); log_errx(1, "getaddrinfo for %s failed: %s", address, gai_strerror(error)); } } static struct connection * connection_new(unsigned int session_id, const uint8_t isid[8], uint16_t tsih, const struct iscsi_session_conf *conf, int iscsi_fd) { struct connection *conn; struct addrinfo *from_ai, *to_ai; const char *from_addr, *to_addr; #ifdef ICL_KERNEL_PROXY struct iscsi_daemon_connect idc; #endif int error, sockbuf; conn = calloc(1, sizeof(*conn)); if (conn == NULL) log_err(1, "calloc"); /* * Default values, from RFC 3720, section 12. */ conn->conn_header_digest = CONN_DIGEST_NONE; conn->conn_data_digest = CONN_DIGEST_NONE; conn->conn_initial_r2t = true; conn->conn_immediate_data = true; conn->conn_max_data_segment_length = 8192; conn->conn_max_burst_length = 262144; conn->conn_first_burst_length = 65536; conn->conn_session_id = session_id; memcpy(&conn->conn_isid, isid, sizeof(conn->conn_isid)); conn->conn_tsih = tsih; conn->conn_iscsi_fd = iscsi_fd; /* * XXX: Should we sanitize this somehow? */ memcpy(&conn->conn_conf, conf, sizeof(conn->conn_conf)); from_addr = conn->conn_conf.isc_initiator_addr; to_addr = conn->conn_conf.isc_target_addr; if (from_addr[0] != '\0') resolve_addr(conn, from_addr, &from_ai, true); else from_ai = NULL; resolve_addr(conn, to_addr, &to_ai, false); #ifdef ICL_KERNEL_PROXY if (conn->conn_conf.isc_iser) { memset(&idc, 0, sizeof(idc)); idc.idc_session_id = conn->conn_session_id; if (conn->conn_conf.isc_iser) idc.idc_iser = 1; idc.idc_domain = to_ai->ai_family; idc.idc_socktype = to_ai->ai_socktype; idc.idc_protocol = to_ai->ai_protocol; if (from_ai != NULL) { idc.idc_from_addr = from_ai->ai_addr; idc.idc_from_addrlen = from_ai->ai_addrlen; } idc.idc_to_addr = to_ai->ai_addr; idc.idc_to_addrlen = to_ai->ai_addrlen; log_debugx("connecting to %s using ICL kernel proxy", to_addr); error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc); if (error != 0) { fail(conn, strerror(errno)); log_err(1, "failed to connect to %s " "using ICL kernel proxy: ISCSIDCONNECT", to_addr); } return (conn); } #endif /* ICL_KERNEL_PROXY */ if (conn->conn_conf.isc_iser) { fail(conn, "iSER not supported"); log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY " "does not support iSER"); } conn->conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, to_ai->ai_protocol); if (conn->conn_socket < 0) { fail(conn, strerror(errno)); log_err(1, "failed to create socket for %s", from_addr); } sockbuf = SOCKBUF_SIZE; if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_RCVBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_RCVBUF) failed"); sockbuf = SOCKBUF_SIZE; if (setsockopt(conn->conn_socket, SOL_SOCKET, SO_SNDBUF, &sockbuf, sizeof(sockbuf)) == -1) log_warn("setsockopt(SO_SNDBUF) failed"); if (from_ai != NULL) { error = bind(conn->conn_socket, from_ai->ai_addr, from_ai->ai_addrlen); if (error != 0) { fail(conn, strerror(errno)); log_err(1, "failed to bind to %s", from_addr); } } log_debugx("connecting to %s", to_addr); error = connect(conn->conn_socket, to_ai->ai_addr, to_ai->ai_addrlen); if (error != 0) { fail(conn, strerror(errno)); log_err(1, "failed to connect to %s", to_addr); } return (conn); } static void handoff(struct connection *conn) { struct iscsi_daemon_handoff idh; int error; log_debugx("handing off connection to the kernel"); memset(&idh, 0, sizeof(idh)); idh.idh_session_id = conn->conn_session_id; idh.idh_socket = conn->conn_socket; strlcpy(idh.idh_target_alias, conn->conn_target_alias, sizeof(idh.idh_target_alias)); idh.idh_tsih = conn->conn_tsih; idh.idh_statsn = conn->conn_statsn; idh.idh_header_digest = conn->conn_header_digest; idh.idh_data_digest = conn->conn_data_digest; idh.idh_initial_r2t = conn->conn_initial_r2t; idh.idh_immediate_data = conn->conn_immediate_data; idh.idh_max_data_segment_length = conn->conn_max_data_segment_length; idh.idh_max_burst_length = conn->conn_max_burst_length; idh.idh_first_burst_length = conn->conn_first_burst_length; error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh); if (error != 0) log_err(1, "ISCSIDHANDOFF"); } void fail(const struct connection *conn, const char *reason) { struct iscsi_daemon_fail idf; int error; memset(&idf, 0, sizeof(idf)); idf.idf_session_id = conn->conn_session_id; strlcpy(idf.idf_reason, reason, sizeof(idf.idf_reason)); error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf); if (error != 0) log_err(1, "ISCSIDFAIL"); } /* * XXX: I CANT INTO LATIN */ static void capsicate(struct connection *conn) { int error; cap_rights_t rights; #ifdef ICL_KERNEL_PROXY const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE, ISCSISMODIFY }; #else const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE, ISCSISMODIFY }; #endif cap_rights_init(&rights, CAP_IOCTL); error = cap_rights_limit(conn->conn_iscsi_fd, &rights); if (error != 0 && errno != ENOSYS) log_err(1, "cap_rights_limit"); error = cap_ioctls_limit(conn->conn_iscsi_fd, cmds, sizeof(cmds) / sizeof(cmds[0])); if (error != 0 && errno != ENOSYS) log_err(1, "cap_ioctls_limit"); error = cap_enter(); if (error != 0 && errno != ENOSYS) log_err(1, "cap_enter"); if (cap_sandboxed()) log_debugx("Capsicum capability mode enabled"); else log_warnx("Capsicum capability mode not supported"); } bool timed_out(void) { return (sigalrm_received); } static void sigalrm_handler(int dummy __unused) { /* * It would be easiest to just log an error and exit. We can't * do this, though, because log_errx() is not signal safe, since * it calls syslog(3). Instead, set a flag checked by pdu_send() * and pdu_receive(), to call log_errx() there. Should they fail * to notice, we'll exit here one second later. */ if (sigalrm_received) { /* * Oh well. Just give up and quit. */ _exit(2); } sigalrm_received = true; } static void set_timeout(int timeout) { struct sigaction sa; struct itimerval itv; int error; if (timeout <= 0) { log_debugx("session timeout disabled"); return; } bzero(&sa, sizeof(sa)); sa.sa_handler = sigalrm_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGALRM, &sa, NULL); if (error != 0) log_err(1, "sigaction"); /* * First SIGALRM will arive after conf_timeout seconds. * If we do nothing, another one will arrive a second later. */ bzero(&itv, sizeof(itv)); itv.it_interval.tv_sec = 1; itv.it_value.tv_sec = timeout; log_debugx("setting session timeout to %d seconds", timeout); error = setitimer(ITIMER_REAL, &itv, NULL); if (error != 0) log_err(1, "setitimer"); } static void sigchld_handler(int dummy __unused) { /* * The only purpose of this handler is to make SIGCHLD * interrupt the ISCSIDWAIT ioctl(2), so we can call * wait_for_children(). */ } static void register_sigchld(void) { struct sigaction sa; int error; bzero(&sa, sizeof(sa)); sa.sa_handler = sigchld_handler; sigfillset(&sa.sa_mask); error = sigaction(SIGCHLD, &sa, NULL); if (error != 0) log_err(1, "sigaction"); } static void handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout) { struct connection *conn; log_set_peer_addr(request->idr_conf.isc_target_addr); if (request->idr_conf.isc_target[0] != '\0') { log_set_peer_name(request->idr_conf.isc_target); setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target); } else { setproctitle("%s", request->idr_conf.isc_target_addr); } conn = connection_new(request->idr_session_id, request->idr_isid, request->idr_tsih, &request->idr_conf, iscsi_fd); set_timeout(timeout); capsicate(conn); login(conn); if (conn->conn_conf.isc_discovery != 0) discovery(conn); else handoff(conn); log_debugx("nothing more to do; exiting"); exit (0); } static int wait_for_children(bool block) { pid_t pid; int status; int num = 0; for (;;) { /* * If "block" is true, wait for at least one process. */ if (block && num == 0) pid = wait4(-1, &status, 0, NULL); else pid = wait4(-1, &status, WNOHANG, NULL); if (pid <= 0) break; if (WIFSIGNALED(status)) { log_warnx("child process %d terminated with signal %d", pid, WTERMSIG(status)); } else if (WEXITSTATUS(status) != 0) { log_warnx("child process %d terminated with exit status %d", pid, WEXITSTATUS(status)); } else { log_debugx("child process %d terminated gracefully", pid); } num++; } return (num); } int main(int argc, char **argv) { int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno, timeout = 60; bool dont_daemonize = false; struct pidfh *pidfh; pid_t pid, otherpid; const char *pidfile_path = DEFAULT_PIDFILE; struct iscsi_daemon_request request; while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) { switch (ch) { case 'P': pidfile_path = optarg; break; case 'd': dont_daemonize = true; debug++; break; case 'l': debug = atoi(optarg); break; case 'm': maxproc = atoi(optarg); break; case 't': timeout = atoi(optarg); break; case '?': default: usage(); } } argc -= optind; if (argc != 0) usage(); log_init(debug); pidfh = pidfile_open(pidfile_path, 0600, &otherpid); if (pidfh == NULL) { if (errno == EEXIST) log_errx(1, "daemon already running, pid: %jd.", (intmax_t)otherpid); log_err(1, "cannot open or create pidfile \"%s\"", pidfile_path); } iscsi_fd = open(ISCSI_PATH, O_RDWR); if (iscsi_fd < 0 && errno == ENOENT) { saved_errno = errno; retval = kldload("iscsi"); if (retval != -1) iscsi_fd = open(ISCSI_PATH, O_RDWR); else errno = saved_errno; } if (iscsi_fd < 0) log_err(1, "failed to open %s", ISCSI_PATH); if (dont_daemonize == false) { if (daemon(0, 0) == -1) { log_warn("cannot daemonize"); pidfile_remove(pidfh); exit(1); } } pidfile_write(pidfh); register_sigchld(); for (;;) { log_debugx("waiting for request from the kernel"); memset(&request, 0, sizeof(request)); error = ioctl(iscsi_fd, ISCSIDWAIT, &request); if (error != 0) { if (errno == EINTR) { nchildren -= wait_for_children(false); assert(nchildren >= 0); continue; } log_err(1, "ISCSIDWAIT"); } if (dont_daemonize) { log_debugx("not forking due to -d flag; " "will exit after servicing a single request"); } else { nchildren -= wait_for_children(false); assert(nchildren >= 0); while (maxproc > 0 && nchildren >= maxproc) { log_debugx("maxproc limit of %d child processes hit; " "waiting for child process to exit", maxproc); nchildren -= wait_for_children(true); assert(nchildren >= 0); } log_debugx("incoming connection; forking child process #%d", nchildren); nchildren++; pid = fork(); if (pid < 0) log_err(1, "fork"); if (pid > 0) continue; } pidfile_close(pidfh); handle_request(iscsi_fd, &request, timeout); } return (0); } Index: stable/10/usr.sbin/rwhod/rwhod.c =================================================================== --- stable/10/usr.sbin/rwhod/rwhod.c (revision 280249) +++ stable/10/usr.sbin/rwhod/rwhod.c (revision 280250) @@ -1,782 +1,782 @@ /*- * Copyright (c) 1983, 1993 The Regents of the University of California. * Copyright (c) 2013 Mariusz Zaborski * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); -#include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNPRIV_USER "daemon" #define UNPRIV_GROUP "daemon" #define NO_MULTICAST 0 /* multicast modes */ #define PER_INTERFACE_MULTICAST 1 #define SCOPED_MULTICAST 2 #define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */ #define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */ /* (belongs in protocols/rwhod.h) */ int insecure_mode; int quiet_mode; int iff_flag = IFF_POINTOPOINT; int multicast_mode = NO_MULTICAST; int multicast_scope; struct sockaddr_in multicast_addr = { sizeof(multicast_addr), AF_INET, 0, { 0 }, { 0 } }; /* * Sleep interval. Don't forget to change the down time check in ruptime * if this is changed. */ #define SL_INTERVAL (3 * 60) char myname[MAXHOSTNAMELEN]; /* * We communicate with each neighbor in a list constructed at the time we're * started up. Neighbors are currently directly connected via a hardware * interface. */ struct neighbor { struct neighbor *n_next; char *n_name; /* interface name */ struct sockaddr *n_addr; /* who to send to */ int n_addrlen; /* size of address */ int n_flags; /* should forward?, interface flags */ }; struct neighbor *neighbors; struct whod mywd; struct servent *sp; int s; int fdp; pid_t pid_child_receiver; #define WHDRSIZE (int)(sizeof(mywd) - sizeof(mywd.wd_we)) int configure(int so); void getboottime(int signo __unused); void receiver_process(void); void rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo); void run_as(uid_t *uid, gid_t *gid); void quit(const char *msg); void sender_process(void); int verify(char *name, int maxlen); static void usage(void); #ifdef DEBUG char *interval(int time, char *updown); void Sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to, int tolen); #define sendto Sendto #endif /* * This version of Berkeley's rwhod has been modified to use IP multicast * datagrams, under control of a new command-line option: * * rwhod -m causes rwhod to use IP multicast (instead of * broadcast or unicast) on all interfaces that have * the IFF_MULTICAST flag set in their "ifnet" structs * (excluding the loopback interface). The multicast * reports are sent with a time-to-live of 1, to prevent * forwarding beyond the directly-connected subnet(s). * * rwhod -m causes rwhod to send IP multicast datagrams with a * time-to-live of , via a SINGLE interface rather * than all interfaces. must be between 0 and * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1" * is different than "-m", in that "-m 1" specifies * transmission on one interface only. * * When "-m" is used without a argument, the program accepts multicast * rwhod reports from all multicast-capable interfaces. If a argument * is given, it accepts multicast reports from only one interface, the one * on which reports are sent (which may be controlled via the host's routing * table). Regardless of the "-m" option, the program accepts broadcast or * unicast reports from all interfaces. Thus, this program will hear the * reports of old, non-multicasting rwhods, but, if multicasting is used, * those old rwhods won't hear the reports generated by this program. * * -- Steve Deering, Stanford University, February 1989 */ int main(int argc, char *argv[]) { int on; char *cp; struct sockaddr_in soin; uid_t unpriv_uid; gid_t unpriv_gid; on = 1; if (getuid()) errx(1, "not super user"); run_as(&unpriv_uid, &unpriv_gid); argv++; argc--; while (argc > 0 && *argv[0] == '-') { if (strcmp(*argv, "-m") == 0) { if (argc > 1 && isdigit(*(argv + 1)[0])) { argv++; argc--; multicast_mode = SCOPED_MULTICAST; multicast_scope = atoi(*argv); if (multicast_scope > MAX_MULTICAST_SCOPE) { errx(1, "ttl must not exceed %u", MAX_MULTICAST_SCOPE); } } else { multicast_mode = PER_INTERFACE_MULTICAST; } } else if (strcmp(*argv, "-i") == 0) { insecure_mode = 1; } else if (strcmp(*argv, "-l") == 0) { quiet_mode = 1; } else if (strcmp(*argv, "-p") == 0) { iff_flag = 0; } else { usage(); } argv++; argc--; } if (argc > 0) usage(); #ifndef DEBUG daemon(1, 0); #endif (void) signal(SIGHUP, getboottime); openlog("rwhod", LOG_PID | LOG_NDELAY, LOG_DAEMON); sp = getservbyname("who", "udp"); if (sp == NULL) { syslog(LOG_ERR, "who/udp: unknown service"); exit(1); } if (chdir(_PATH_RWHODIR) < 0) { syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR); exit(1); } /* * Establish host name as returned by system. */ if (gethostname(myname, sizeof(myname) - 1) < 0) { syslog(LOG_ERR, "gethostname: %m"); exit(1); } if ((cp = strchr(myname, '.')) != NULL) *cp = '\0'; strlcpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname)); getboottime(0); if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "socket: %m"); exit(1); } if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m"); exit(1); } memset(&soin, 0, sizeof(soin)); soin.sin_len = sizeof(soin); soin.sin_family = AF_INET; soin.sin_port = sp->s_port; if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) { syslog(LOG_ERR, "bind: %m"); exit(1); } if (setgid(unpriv_gid) != 0) { syslog(LOG_ERR, "setgid: %m"); exit(1); } if (setgroups(1, &unpriv_gid) != 0) { /* XXX BOGUS groups[0] = egid */ syslog(LOG_ERR, "setgroups: %m"); exit(1); } if (setuid(unpriv_uid) != 0) { syslog(LOG_ERR, "setuid: %m"); exit(1); } if (!configure(s)) exit(1); if (!quiet_mode) { pid_child_receiver = pdfork(&fdp, 0); if (pid_child_receiver == 0) { receiver_process(); } else if (pid_child_receiver > 0) { sender_process(); } else if (pid_child_receiver == -1) { if (errno == ENOSYS) { syslog(LOG_ERR, "The pdfork(2) system call is not available; recompile the kernel with options PROCDESC"); } else { syslog(LOG_ERR, "pdfork: %m"); } exit(1); } } else { receiver_process(); } } static void usage(void) { fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n"); exit(1); } void run_as(uid_t *uid, gid_t *gid) { struct passwd *pw; struct group *gr; pw = getpwnam(UNPRIV_USER); if (pw == NULL) { syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER); exit(1); } *uid = pw->pw_uid; gr = getgrnam(UNPRIV_GROUP); if (gr == NULL) { syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP); exit(1); } *gid = gr->gr_gid; } /* * Check out host name for unprintables * and other funnies before allowing a file * to be created. Sorry, but blanks aren't allowed. */ int verify(char *name, int maxlen) { int size; size = 0; while (*name != '\0' && size < maxlen - 1) { if (!isascii((unsigned char)*name) || !(isalnum((unsigned char)*name) || ispunct((unsigned char)*name))) { return (0); } name++; size++; } *name = '\0'; return (size > 0); } void receiver_process(void) { struct sockaddr_in from; struct stat st; cap_rights_t rights; char path[64]; int dirfd; struct whod wd; socklen_t len; int cc, whod; time_t t; len = sizeof(from); dirfd = open(".", O_RDONLY | O_DIRECTORY); if (dirfd < 0) { syslog(LOG_WARNING, "%s: %m", _PATH_RWHODIR); exit(1); } cap_rights_init(&rights, CAP_CREATE, CAP_FSTAT, CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE); if (cap_rights_limit(dirfd, &rights) < 0 && errno != ENOSYS) { syslog(LOG_WARNING, "cap_rights_limit: %m"); exit(1); } if (cap_enter() < 0 && errno != ENOSYS) { syslog(LOG_ERR, "cap_enter: %m"); exit(1); } for (;;) { cc = recvfrom(s, &wd, sizeof(wd), 0, (struct sockaddr *)&from, &len); if (cc <= 0) { if (cc < 0 && errno != EINTR) syslog(LOG_WARNING, "recv: %m"); continue; } if (from.sin_port != sp->s_port && !insecure_mode) { syslog(LOG_WARNING, "%d: bad source port from %s", ntohs(from.sin_port), inet_ntoa(from.sin_addr)); continue; } if (cc < WHDRSIZE) { syslog(LOG_WARNING, "short packet from %s", inet_ntoa(from.sin_addr)); continue; } if (wd.wd_vers != WHODVERSION) continue; if (wd.wd_type != WHODTYPE_STATUS) continue; if (!verify(wd.wd_hostname, sizeof(wd.wd_hostname))) { syslog(LOG_WARNING, "malformed host name from %s", inet_ntoa(from.sin_addr)); continue; } (void) snprintf(path, sizeof(path), "whod.%s", wd.wd_hostname); /* * Rather than truncating and growing the file each time, * use ftruncate if size is less than previous size. */ whod = openat(dirfd, path, O_WRONLY | O_CREAT, 0644); if (whod < 0) { syslog(LOG_WARNING, "%s: %m", path); continue; } cap_rights_init(&rights, CAP_FSTAT, CAP_FTRUNCATE, CAP_WRITE); if (cap_rights_limit(whod, &rights) < 0 && errno != ENOSYS) { syslog(LOG_WARNING, "cap_rights_limit: %m"); exit(1); } #if ENDIAN != BIG_ENDIAN { struct whoent *we; int i, n; n = (cc - WHDRSIZE) / sizeof(struct whoent); /* undo header byte swapping before writing to file */ wd.wd_sendtime = ntohl(wd.wd_sendtime); for (i = 0; i < 3; i++) wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]); wd.wd_boottime = ntohl(wd.wd_boottime); we = wd.wd_we; for (i = 0; i < n; i++) { we->we_idle = ntohl(we->we_idle); we->we_utmp.out_time = ntohl(we->we_utmp.out_time); we++; } } #endif (void) time(&t); wd.wd_recvtime = _time_to_int(t); (void) write(whod, (char *)&wd, cc); if (fstat(whod, &st) < 0 || st.st_size > cc) ftruncate(whod, cc); (void) close(whod); } (void) close(dirfd); } void sender_process(void) { int sendcount; double avenrun[3]; time_t now; int i, cc, status; struct utmpx *ut; struct stat stb; struct neighbor *np; struct whoent *we, *wend; sendcount = 0; for (;;) { we = mywd.wd_we; now = time(NULL); if (sendcount % 10 == 0) getboottime(0); sendcount++; wend = &mywd.wd_we[1024 / sizeof(struct whoent)]; setutxent(); while ((ut = getutxent()) != NULL && we < wend) { if (ut->ut_type != USER_PROCESS) continue; strncpy(we->we_utmp.out_line, ut->ut_line, sizeof(we->we_utmp.out_line)); strncpy(we->we_utmp.out_name, ut->ut_user, sizeof(we->we_utmp.out_name)); we->we_utmp.out_time = htonl(_time_to_time32(ut->ut_tv.tv_sec)); we++; } endutxent(); if (chdir(_PATH_DEV) < 0) { syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV); exit(1); } wend = we; for (we = mywd.wd_we; we < wend; we++) { if (stat(we->we_utmp.out_line, &stb) >= 0) we->we_idle = htonl(now - stb.st_atime); we++; } (void) getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])); for (i = 0; i < 3; i++) mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100)); cc = (char *)wend - (char *)&mywd; mywd.wd_sendtime = htonl(_time_to_time32(time(NULL))); mywd.wd_vers = WHODVERSION; mywd.wd_type = WHODTYPE_STATUS; if (multicast_mode == SCOPED_MULTICAST) { (void) sendto(s, (char *)&mywd, cc, 0, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr)); } else { for (np = neighbors; np != NULL; np = np->n_next) { if (multicast_mode == PER_INTERFACE_MULTICAST && (np->n_flags & IFF_MULTICAST) != 0) { /* * Select the outgoing interface for the * multicast. */ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &(((struct sockaddr_in *)np->n_addr)->sin_addr), sizeof(struct in_addr)) < 0) { syslog(LOG_ERR, "setsockopt IP_MULTICAST_IF: %m"); exit(1); } (void) sendto(s, (char *)&mywd, cc, 0, (struct sockaddr *)&multicast_addr, sizeof(multicast_addr)); } else { (void) sendto(s, (char *)&mywd, cc, 0, np->n_addr, np->n_addrlen); } } } if (chdir(_PATH_RWHODIR) < 0) { syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR); exit(1); } if (waitpid(pid_child_receiver, &status, WNOHANG) == pid_child_receiver) { break; } sleep(SL_INTERVAL); } } void getboottime(int signo __unused) { int mib[2]; size_t size; struct timeval tm; mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; size = sizeof(tm); if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) { syslog(LOG_ERR, "cannot get boottime: %m"); exit(1); } mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec)); } void quit(const char *msg) { syslog(LOG_ERR, "%s", msg); exit(1); } void rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo) { struct sockaddr *sa; int i; memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info)); for (i = 0; i < RTAX_MAX && cp < cplim; i++) { if ((rtinfo->rti_addrs & (1 << i)) == 0) continue; sa = (struct sockaddr *)cp; rtinfo->rti_info[i] = sa; cp += SA_SIZE(sa); } } /* * Figure out device configuration and select * networks which deserve status information. */ int configure(int so) { struct neighbor *np; struct if_msghdr *ifm; struct ifa_msghdr *ifam; struct sockaddr_dl *sdl; size_t needed; int mib[6], flags, lflags, len; char *buf, *lim, *next; struct rt_addrinfo info; flags = 0; if (multicast_mode != NO_MULTICAST) { multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP); multicast_addr.sin_port = sp->s_port; } if (multicast_mode == SCOPED_MULTICAST) { struct ip_mreq mreq; unsigned char ttl; mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { syslog(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP: %m"); return (0); } ttl = multicast_scope; if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) { syslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL: %m"); return (0); } return (1); } mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; mib[3] = AF_INET; mib[4] = NET_RT_IFLIST; mib[5] = 0; if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) quit("route-sysctl-estimate"); if ((buf = malloc(needed)) == NULL) quit("malloc"); if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) quit("actual retrieval of interface table"); lim = buf + needed; sdl = NULL; /* XXX just to keep gcc -Wall happy */ for (next = buf; next < lim; next += ifm->ifm_msglen) { ifm = (struct if_msghdr *)next; if (ifm->ifm_type == RTM_IFINFO) { sdl = (struct sockaddr_dl *)(ifm + 1); flags = ifm->ifm_flags; continue; } if ((flags & IFF_UP) == 0) continue; lflags = IFF_BROADCAST | iff_flag; if (multicast_mode == PER_INTERFACE_MULTICAST) lflags |= IFF_MULTICAST; if ((flags & lflags) == 0) continue; if (ifm->ifm_type != RTM_NEWADDR) quit("out of sync parsing NET_RT_IFLIST"); ifam = (struct ifa_msghdr *)ifm; info.rti_addrs = ifam->ifam_addrs; rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, &info); /* gag, wish we could get rid of Internet dependencies */ #define dstaddr info.rti_info[RTAX_BRD] #define ifaddr info.rti_info[RTAX_IFA] #define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr #define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port if (dstaddr == 0 || dstaddr->sa_family != AF_INET) continue; PORT_SA(dstaddr) = sp->s_port; for (np = neighbors; np != NULL; np = np->n_next) { if (memcmp(sdl->sdl_data, np->n_name, sdl->sdl_nlen) == 0 && IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr)) { break; } } if (np != NULL) continue; len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1; np = malloc(len); if (np == NULL) quit("malloc of neighbor structure"); memset(np, 0, len); np->n_flags = flags; np->n_addr = (struct sockaddr *)(np + 1); np->n_addrlen = dstaddr->sa_len; np->n_name = np->n_addrlen + (char *)np->n_addr; memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen); memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen); if (multicast_mode == PER_INTERFACE_MULTICAST && (flags & IFF_MULTICAST) != 0 && (flags & IFF_LOOPBACK) == 0) { struct ip_mreq mreq; memcpy((char *)np->n_addr, (char *)ifaddr, np->n_addrlen); mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP); mreq.imr_interface.s_addr = ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr; if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { syslog(LOG_ERR, "setsockopt IP_ADD_MEMBERSHIP: %m"); #if 0 /* Fall back to broadcast on this if. */ np->n_flags &= ~IFF_MULTICAST; #else free(np); continue; #endif } } np->n_next = neighbors; neighbors = np; } free(buf); return (1); } #ifdef DEBUG void Sendto(int s, const void *buf, size_t cc, int flags, const struct sockaddr *to, int tolen) { struct whod *w; struct whoent *we; struct sockaddr_in *sin; w = (struct whod *)buf; sin = (struct sockaddr_in *)to; printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr), ntohs(sin->sin_port)); printf("hostname %s %s\n", w->wd_hostname, interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up")); printf("load %4.2f, %4.2f, %4.2f\n", ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0, ntohl(w->wd_loadav[2]) / 100.0); cc -= WHDRSIZE; for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) { time_t t = _time32_to_time(ntohl(we->we_utmp.out_time)); printf("%-8.8s %s:%s %.12s", we->we_utmp.out_name, w->wd_hostname, we->we_utmp.out_line, ctime(&t) + 4); we->we_idle = ntohl(we->we_idle) / 60; if (we->we_idle != 0) { if (we->we_idle >= 100 * 60) we->we_idle = 100 * 60 - 1; if (we->we_idle >= 60) printf(" %2d", we->we_idle / 60); else printf(" "); printf(":%02d", we->we_idle % 60); } printf("\n"); } } char * interval(int time, char *updown) { static char resbuf[32]; int days, hours, minutes; if (time < 0 || time > 3 * 30 * 24 * 60 * 60) { (void) sprintf(resbuf, " %s ??:??", updown); return (resbuf); } minutes = (time + 59) / 60; /* round to minutes */ hours = minutes / 60; minutes %= 60; days = hours / 24; hours %= 24; if (days > 0) { (void) sprintf(resbuf, "%s %2d+%02d:%02d", updown, days, hours, minutes); } else { (void) sprintf(resbuf, "%s %2d:%02d", updown, hours, minutes); } return (resbuf); } #endif Index: stable/10 =================================================================== --- stable/10 (revision 280249) +++ stable/10 (revision 280250) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r263234