diff --git a/en_US.ISO8859-1/books/arch-handbook/boot/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/boot/chapter.sgml index 9e8ae2ef1c..0e28c6519e 100644 --- a/en_US.ISO8859-1/books/arch-handbook/boot/chapter.sgml +++ b/en_US.ISO8859-1/books/arch-handbook/boot/chapter.sgml @@ -1,1023 +1,1024 @@ Sergey Lyubka Contributed by Bootstrapping and kernel initialization Synopsis This chapter is an overview of the boot and system initialization process, starting from the BIOS (firmware) POST, to the first user process creation. Since the initial steps of system startup are very architecture dependent, the IA-32 architecture is used as an example. Overview A computer running FreeBSD can boot by several methods, although the most common method, booting from a harddisk where the OS is installed, will be discussed here. The boot process is divided into several steps: BIOS POST boot0 stage boot2 stage loader stage kernel initialization The boot0 and boot2 stages are also referred to as bootstrap stages 1 and 2 in &man.boot.8; as the first steps in FreeBSD's 3-stage bootstrapping procedure. Various information is printed on the screen at each stage, so you may visually recognize them using the table that follows. Please note that the actual data may differ from machine to machine: may vary BIOS (firmware) messages F1 FreeBSD F2 BSD F5 Disk 2 boot0 >>FreeBSD/i386 BOOT Default: 1:ad(1,a)/boot/loader boot: boot2This prompt will appear if the user presses a key just after selecting an OS to boot at the boot0 stage. BTX loader 1.0 BTX version is 1.01 BIOS drive A: is disk0 BIOS drive C: is disk1 BIOS 639kB/64512kB available memory FreeBSD/i386 bootstrap loader, Revision 0.8 Console internal video/keyboard (jkh@bento.freebsd.org, Mon Nov 20 11:41:23 GMT 2000) /kernel text=0x1234 data=0x2345 syms=[0x4+0x3456] Hit [Enter] to boot immediately, or any other key for command prompt Booting [kernel] in 9 seconds..._ loader Copyright (c) 1992-2002 The FreeBSD Project. Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 The Regents of the University of California. All rights reserved. FreeBSD 4.6-RC #0: Sat May 4 22:49:02 GMT 2002 devnull@kukas:/usr/obj/usr/src/sys/DEVNULL Timecounter "i8254" frequency 1193182 Hz kernel BIOS POST When the PC powers on, the processor's registers are set to some predefined values. One of the registers is the instruction pointer register, and its value after a power on is well defined: it is a 32-bit value of 0xfffffff0. The instruction pointer register points to code to be executed by the processor. One of the registers is the cr1 32-bit control register, and its value just after the reboot is 0. One of the cr1's bits, the bit PE (Protected Enabled) indicates whether the processor is running in protected or real mode. Since at boot time this bit is cleared, the processor boots in real mode. Real mode means, among other things, that linear and physical addresses are identical. The value of 0xfffffff0 is slightly less then 4Gb, so unless the machine has 4Gb physical memory, it cannot point to a valid memory address. The computer's hardware translates this address so that it points to a BIOS memory block. BIOS stands for Basic Input Output System, and it is a chip on the motherboard that has a relatively small amount of read-only memory (ROM). This memory contains various low-level routines that are specific to the hardware supplied with the motherboard. So, the processor will first jump to the address 0xfffffff0, which really resides in the BIOS's memory. Usually this address contains a jump instruction to the BIOS's POST routines. POST stands for Power On Self Test. This is a set of routines including the memory check, system bus check and other low-level stuff so that the CPU can initialize the computer properly. The important step on this stage is determining the boot device. All modern BIOS's allow the boot device to be set manually, so you can boot from a floppy, CD-ROM, harddisk etc. The very last thing in the POST is the INT 0x19 instruction. That instruction reads 512 bytes from the first sector of boot device into the memory at address 0x7c00. The term first sector originates from harddrive architecture, where the magnetic plate is divided to a number of cylindrical tracks. Tracks are numbered, and every track is divided by a number (usually 64) sectors. Track number 0 is the outermost on the magnetic plate, and sector 1, the first sector (tracks, or, cylinders, are numbered starting from 0, but sectors - starting from 1), has a special meaning. It is also called Master Boot Record, or MBR. The remaining sectors on the first track are never used Some utilities such as &man.disklabel.8; may store the information in this area, mostly in the second sector.. <literal>boot0</literal> stage Take a look at the file /boot/boot0. This is a small 512-byte file, and it is exactly what FreeBSD's installation procedure wrote to your harddisk's MBR if you chose the bootmanager option at installation time. As mentioned previously, the INT 0x19 instruction loads an MBR, i.e. the boot0 content, into the memory at address 0x7c00. Taking a look at the file sys/boot/i386/boot0/boot0.s can give a guess at what is happening there - this is the boot manager, which is an awesome piece of code written by Robert Nordier. The MBR, or, boot0, has a special structure starting from offset 0x1be, called the partition table. It has 4 records of 16 bytes each, called partition records, which represent how the harddisk(s) are partitioned, or, in FreeBSD's terminology, sliced. One byte of those 16 says whether a partition (slice) is bootable or not. Exactly one record must have that flag set, otherwise boot0's code will refuse to proceed. A partition record has the following fields: the 1-byte filesystem type the 1-byte bootable flag the 6 byte descriptor in CHS format the 8 byte descriptor in LBA format A partition record descriptor has the information about where exactly the partition resides on the drive. Both descriptors, LBA and CHS, describe the same information, but in different ways: LBA (Logical Block Addressing) has the starting sector for the partition and the partition's length, while CHS (Cylinder Head Sector) has coordinates for the first and last sectors of the partition. The boot manager scans the partition table and prints the menu on the screen so the user can select what disk and what slice to boot. By pressing an appropriate key, boot0 performs the following actions: modifies the bootable flag for the selected partition to make it bootable, and clears the previous saves itself to disk to remember what partition (slice) has been selected so to use it as the default on the next boot loads the first sector of the selected partition (slice) into memory and jumps there What kind of data should reside on the very first sector of a bootable partition (slice), in our case, a FreeBSD slice? As you may have already guessed, it is boot2. <literal>boot2</literal> stage You might wonder, why boot2 comes after boot0, and not boot1. Actually, there is a 512-byte file called boot1 in the directory /boot as well. It is used for booting from a floppy. When booting from a floppy, boot1 plays the same role as boot0 for a harddisk: it locates boot2 and runs it. You may have realized that a file /boot/mbr exists as well. It is a simplified version of boot0. The code in mbr does not provide a menu for the user, it just blindly boots the partition marked active. The code implementing boot2 resides in sys/boot/i386/boot2/, and the executable itself is in /boot. The files boot0 and boot2 that are in /boot are not used by the bootstrap, but by utilities such as boot0cfg. The actual position for boot0 is in the MBR. For boot2 it is the beginning of a bootable FreeBSD slice. These locations are not under the filesystem's control, so they are invisible to commands like ls. The main task for boot2 is to load the file /boot/loader, which is the third stage in the bootstrapping procedure. The code in boot2 cannot use any services like open() and read(), since the kernel is not yet loaded. It must scan the harddisk, knowing about the filesystem structure, find the file /boot/loader, read it into memory using a BIOS service, and then pass the execution to the loader's entry point. Besides that, boot2 prompts for user input so the loader can be booted from different disk, unit, slice and partition. The boot2 binary is created in special way: sys/boot/i386/boot2/Makefile boot2: boot2.ldr boot2.bin ${BTX}/btx/btx btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr \ -o boot2.ld -P 1 boot2.bin This Makefile snippet shows that &man.btxld.8; is used to link the binary. BTX, which stands for BooT eXtender, is a piece of code that provides a protected mode environment for the program, called the client, that it is linked with. So boot2 is a BTX client, i.e. it uses the service provided by BTX. The btxld utility is the linker. It links two binaries together. The difference between &man.btxld.8; and &man.ld.1; is that ld usually links object files into a shared object or executable, while btxld links an object file with the BTX, producing the binary file suitable to be put on the beginning of the partition for the system boot. boot0 passes the execution to BTX's entry point. BTX then switches the processor to protected mode, and prepares a simple environment before calling the client. This includes: virtual v86 mode. That means, the BTX is a v86 monitor. Real mode instructions like posh, popf, cli, sti, if called by the client, will work. Interrupt Descriptor Table (IDT) is set up so all hardware interrupts are routed to the default BIOS's handlers, and interrupt 0x30 is set up to be the syscall gate. Two system calls: exec and exit, are defined: sys/boot/i386/btx/lib/btxsys.s: .set INT_SYS,0x30 # Interrupt number # # System call: exit # __exit: xorl %eax,%eax # BTX system int $INT_SYS # call 0x0 # # System call: exec # __exec: movl $0x1,%eax # BTX system int $INT_SYS # call 0x1 BTX creates a Global Descriptor Table (GDT): sys/boot/i386/btx/btx/btx.s: gdt: .word 0x0,0x0,0x0,0x0 # Null entry .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS The client's code and data start from address MEM_USR (0xa000), and a selector (SEL_UCODE) points to the client's code segment. The SEL_UCODE descriptor has Descriptor Privilege Level (DPL) 3, which is the lowest privilege level. But the INT 0x30 instruction handler resides in a segment pointed to by the SEL_SCODE (supervisor code) selector, as shown from the code that creates an IDT: mov $SEL_SCODE,%dh # Segment selector init.2: shr %bx # Handle this int? jnc init.3 # No mov %ax,(%di) # Set handler offset mov %dh,0x2(%di) # and selector mov %dl,0x5(%di) # Set P:DPL:type add $0x4,%ax # Next handler So, when the client calls __exec(), the code will be executed with the highest privileges. This allows the kernel to change the protected mode data structures, such as page tables, GDT, IDT, etc later, if needed. boot2 defines an important structure, struct bootinfo. This structure is initialized by boot2 and passed to the loader, and then further to the kernel. Some nodes of this structures are set by boot2, the rest by the loader. This structure, among other information, contains the kernel filename, BIOS harddisk geometry, BIOS drive number for boot device, physical memory available, envp pointer etc. The definition for it is: /usr/include/machine/bootinfo.h struct bootinfo { u_int32_t bi_version; u_int32_t bi_kernelname; /* represents a char * */ u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */ /* End of fields that are always present. */ #define bi_endcommon bi_n_bios_used u_int32_t bi_n_bios_used; u_int32_t bi_bios_geom[N_BIOS_GEOM]; u_int32_t bi_size; u_int8_t bi_memsizes_valid; u_int8_t bi_bios_dev; /* bootdev BIOS unit number */ u_int8_t bi_pad[2]; u_int32_t bi_basemem; u_int32_t bi_extmem; u_int32_t bi_symtab; /* struct symtab * */ u_int32_t bi_esymtab; /* struct symtab * */ /* Items below only from advanced bootloader */ u_int32_t bi_kernend; /* end of kernel space */ u_int32_t bi_envp; /* environment */ u_int32_t bi_modulep; /* preloaded modules */ }; boot2 enters into an infinite loop waiting for user input, then calls load(). If the user does not press anything, the loop brakes by a timeout, so load() will load the default file (/boot/loader). Functions ino_t lookup(char *filename) and int xfsread(ino_t inode, void *buf, size_t nbyte) are used to read the content of a file into memory. /boot/loader is an ELF binary, but where the ELF header is prepended with a.out's struct exec structure. load() scans the loader's ELF header, loading the content of /boot/loader into memory, and passing the execution to the loader's entry: sys/boot/i386/boot2/boot2.c: __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), 0, 0, 0, VTOP(&bootinfo)); <application>loader</application> stage loader is a BTX client as well. I will not describe it here in detail, there is a comprehensive manpage written by Mike Smith, &man.loader.8;. The underlying mechanisms and BTX were discussed above. The main task for the loader is to boot the kernel. When the kernel is loaded into memory, it is being called by the loader: sys/boot/common/boot.c: /* Call the exec handler from the loader matching the kernel */ module_formats[km->m_loader]->l_exec(km); Kernel initialization To where exactly is the execution passed by the loader, i.e. what is the kernel's actual entry point. Let us take a look at the command that links the kernel: sys/conf/Makefile.i386: ld -elf -Bdynamic -T /usr/src/sys/conf/ldscript.i386 -export-dynamic \ -dynamic-linker /red/herring -o kernel -X locore.o \ <lots of kernel .o files> A few interesting things can be seen in this line. First, the kernel is an ELF dynamically linked binary, but the dynamic linker for kernel is /red/herring, which is definitely a bogus file. Second, taking a look at the file sys/conf/ldscript.i386 gives an idea about what ld options are used when compiling a kernel. Reading through the first few lines, the string sys/conf/ldscript.i386: ENTRY(btext) says that a kernel's entry point is the symbol `btext'. This symbol is defined in locore.s: sys/i386/i386/locore.s: .text /********************************************************************** * * This is where the bootblocks start us, set the ball rolling... * */ NON_GPROF_ENTRY(btext) First what is done is the register EFLAGS is set to a predefined value of 0x00000002, and then all the segment registers are initialized: sys/i386/i386/locore.s /* Don't trust what the BIOS gives for eflags. */ pushl $PSL_KERNEL popfl /* * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap * to set %cs, %ds, %es and %ss. */ mov %ds, %ax mov %ax, %fs mov %ax, %gs btext calls the routines recover_bootinfo(), identify_cpu(), create_pagetables(), which are also defined in locore.s. Here is a description of what they do: recover_bootinfo This routine parses the parameters to the kernel passed from the bootstrap. The kernel may have been booted in 3 ways: by the loader, described above, by the old disk boot blocks, and by the old diskless boot procedure. This function determines the booting method, and stores the struct bootinfo structure into the kernel memory. identify_cpu This functions tries to find out what CPU it is running on, storing the value found in a variable _cpu. create_pagetables This function allocates and fills out a Page Table Directory at the top of the kernel memory area. + The next steps are enabling VME, if the CPU supports it: testl $CPUID_VME, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_VME, %eax movl %eax, %cr4 Then, enabling paging: /* Now enable paging */ movl R(_IdlePTD), %eax movl %eax,%cr3 /* load ptd addr into mmu */ movl %cr0,%eax /* get control word */ orl $CR0_PE|CR0_PG,%eax /* enable paging */ movl %eax,%cr0 /* and let's page NOW! */ The next three lines of code are because the paging was set, so the jump is needed to continue the execution in virtualized address space: pushl $begin /* jump to high virtualized address */ ret /* now running relocated at KERNBASE where the system is linked to run */ begin: The function init386() is called, with a pointer to the first free physical page, after that mi_startup(). init386 is an architecture dependent initialization function, and mi_startup() is an architecture independent one (the 'mi_' prefix stands for Machine Independent). The kernel never returns from mi_startup(), and by calling it, the kernel finishes booting: sys/i386/i386/locore.s: movl physfree, %esi pushl %esi /* value of first for init386(first) */ call _init386 /* wire 386 chip for unix operation */ call _mi_startup /* autoconfiguration, mountroot etc */ hlt /* never returns to here */ <function>init386()</function> init386() is defined in sys/i386/i386/machdep.c and performs low-level initialization, specific to the i386 chip. The switch to protected mode was performed by the loader. The loader has created the very first task, in which the kernel continues to operate. Before running straight away to the code, I will enumerate the tasks the processor must complete to initialize protected mode execution: Initialize the kernel tunable parameters, passed from the bootstrapping program. Prepare the GDT. Prepare the IDT. Initialize the system console. Initialize the DDB, if it is compiled into kernel. Initialize the TSS. Prepare the LDT. Setup proc0's pcb. What init386() first does is initialize the tunable parameters passed from bootstrap. This is done by setting the environment pointer (envp) and calling init_param1(). The envp pointer has been passed from loader in the bootinfo structure: sys/i386/i386/machdep.c: kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* Init basic tunables, hz etc */ init_param1(); init_param1() is defined in sys/kern/subr_param.c. That file has a number of sysctls, and two functions, init_param1() and init_param2(), that are called from init386(): sys/kern/subr_param.c hz = HZ; TUNABLE_INT_FETCH("kern.hz", &hz); TUNABLE_<typename>_FETCH is used to fetch the value from the environment: /usr/src/sys/sys/kernel.h #define TUNABLE_INT_FETCH(path, var) getenv_int((path), (var)) Sysctl kern.hz is the system clock tick. Along with this, the following sysctls are set by init_param1(): kern.maxswzone, kern.maxbcache, kern.maxtsiz, kern.dfldsiz, kern.dflssiz, kern.maxssiz, kern.sgrowsiz. Then init386() prepares the Global Descriptors Table (GDT). Every task on an x86 is running in its own virtual address space, and this space is addressed by a segment:offset pair. Say, for instance, the current instruction to be executed by the processor lies at CS:EIP, then the linear virtual address for that instruction would be the virtual address of code segment CS + EIP. For convenience, segments begin at virtual address 0 and end at a 4Gb boundary. Therefore, the instruction's linear virtual address for this example would just be the value of EIP. Segment registers such as CS, DS etc are the selectors, i.e. indexes, into GDT (to be more precise, an index is not a selector itself, but the INDEX field of a selector). FreeBSD's GDT holds descriptors for 15 selectors per CPU: sys/i386/i386/machdep.c: union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ sys/i386/include/segments.h: /* * Entries in the Global Descriptor Table (GDT) */ #define GNULL_SEL 0 /* Null Descriptor */ #define GCODE_SEL 1 /* Kernel Code Descriptor */ #define GDATA_SEL 2 /* Kernel Data Descriptor */ #define GPRIV_SEL 3 /* SMP Per-Processor Private Data */ #define GPROC0_SEL 4 /* Task state process slot zero and up */ #define GLDT_SEL 5 /* LDT - eventually one per process */ #define GUSERLDT_SEL 6 /* User LDT */ #define GTGATE_SEL 7 /* Process task switch gate */ #define GBIOSLOWMEM_SEL 8 /* BIOS low memory access (must be entry 8) */ #define GPANIC_SEL 9 /* Task state to consider panic from */ #define GBIOSCODE32_SEL 10 /* BIOS interface (32bit Code) */ #define GBIOSCODE16_SEL 11 /* BIOS interface (16bit Code) */ #define GBIOSDATA_SEL 12 /* BIOS interface (Data) */ #define GBIOSUTIL_SEL 13 /* BIOS interface (Utility) */ #define GBIOSARGS_SEL 14 /* BIOS interface (Arguments) */ Note that those #defines are not selectors themselves, but just a field INDEX of a selector, so they are exactly the indices of the GDT. for example, an actual selector for the kernel code (GCODE_SEL) has the value 0x08. The next step is to initialize the Interrupt Descriptor Table (IDT). This table is to be referenced by the processor when a software or hardware interrupt occurs. For example, to make a system call, user application issues the INT 0x80 instruction. This is a software interrupt, so the processor's hardware looks up a record with index 0x80 in the IDT. This record points to the routine that handles this interrupt, in this particular case, this will be the kernel's syscall gate. The IDT may have a maximum of 256 (0x100) records. The kernel allocates NIDT records for the IDT, where NIDT is the maximum (256): sys/i386/i386/machdep.c: static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ For each interrupt, an appropriate handler is set. The syscall gate for INT 0x80 is set as well: sys/i386/i386/machdep.c: setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); So when a userland application issues the INT 0x80 instruction, control will transfer to the function _Xint0x80_syscall, which is in the kernel code segment and will be executed with supervisor privileges. Console and DDB are then initialized: sys/i386/i386/machdep.c: cninit(); /* skipped */ #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif The Task State Segment is another x86 protected mode structure, the TSS is used by the hardware to store task information when a task switch occurs. The Local Descriptors Table is used to reference userland code and data. Several selectors are defined to point to the LDT, they are the system call gates and the user code and data selectors: /usr/include/machine/segments.h #define LSYS5CALLS_SEL 0 /* forced by intel BCS */ #define LSYS5SIGR_SEL 1 #define L43BSDCALLS_SEL 2 /* notyet */ #define LUCODE_SEL 3 #define LSOL26CALLS_SEL 4 /* Solaris >= 2.6 system call gate */ #define LUDATA_SEL 5 /* separate stack, es,fs,gs sels ? */ /* #define LPOSIXCALLS_SEL 5*/ /* notyet */ #define LBSDICALLS_SEL 16 /* BSDI system call gate */ #define NLDT (LBSDICALLS_SEL + 1) Next, proc0's Process Control Block (struct pcb) structure is initialized. proc0 is a struct proc structure that describes a kernel process. It is always present while the kernel is running, therefore it is declared as global: sys/kern/kern_init.c: struct proc proc0; The structure struct pcb is a part of a proc structure. It is defined in /usr/include/machine/pcb.h and has a process's information specific to the i386 architecture, such as registers values. <function>mi_startup()</function> This function performs a bubble sort of all the system initialization objects and then calls the entry of each object one by one: sys/kern/init_main.c: for (sipp = sysinit; *sipp; sipp++) { /* ... skipped ... */ /* Call function */ (*((*sipp)->func))((*sipp)->udata); /* ... skipped ... */ } Although the sysinit framework is described in the Developers' Handbook, I will discuss the internals of it. Every system initialization object (sysinit object) is created by calling a SYSINIT() macro. Let us take as example an announce sysinit object. This object prints the copyright message: sys/kern/init_main.c: static void print_caddr_t(void *data __unused) { printf("%s", (char *)data); } SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright) The subsystem ID for this object is SI_SUB_COPYRIGHT (0x0800001), which comes right after the SI_SUB_CONSOLE (0x0800000). So, the copyright message will be printed out first, just after the console initialization. Let us take a look at what exactly the macro SYSINIT() does. It expands to a C_SYSINIT() macro. The C_SYSINIT() macro then expands to a static struct sysinit structure declaration with another DATA_SET macro call: /usr/include/sys/kernel.h: #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ static struct sysinit uniquifier ## _sys_init = { \ subsystem, \ order, \ func, \ ident \ }; \ DATA_SET(sysinit_set,uniquifier ## _sys_init); #define SYSINIT(uniquifier, subsystem, order, func, ident) \ C_SYSINIT(uniquifier, subsystem, order, \ (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)ident) The DATA_SET() macro expands to a MAKE_SET(), and that macro is the point where the all sysinit magic is hidden: /usr/include/linker_set.h #define MAKE_SET(set, sym) \ static void const * const __set_##set##_sym_##sym = &sym; \ __asm(".section .set." #set ",\"aw\""); \ __asm(".long " #sym); \ __asm(".previous") #endif #define TEXT_SET(set, sym) MAKE_SET(set, sym) #define DATA_SET(set, sym) MAKE_SET(set, sym) In our case, the following declaration will occur: static struct sysinit announce_sys_init = { SI_SUB_COPYRIGHT, SI_ORDER_FIRST, (sysinit_cfunc_t)(sysinit_nfunc_t) print_caddr_t, (void *) copyright }; static void const *const __set_sysinit_set_sym_announce_sys_init = &announce_sys_init; __asm(".section .set.sysinit_set" ",\"aw\""); __asm(".long " "announce_sys_init"); __asm(".previous"); The first __asm instruction will create an ELF section within the kernel's executable. This will happen at kernel link time. The section will have the name .set.sysinit_set. The content of this section is one 32-bit value, the address of announce_sys_init structure, and that is what the second __asm is. The third __asm instruction marks the end of a section. If a directive with the same section name occurred before, the content, i.e. the 32-bit value, will be appended to the existing section, so forming an array of 32-bit pointers. Running objdump on a kernel binary, you may notice the presence of such small sections: &prompt.user; objdump -h /kernel 7 .set.cons_set 00000014 c03164c0 c03164c0 002154c0 2**2 CONTENTS, ALLOC, LOAD, DATA 8 .set.kbddriver_set 00000010 c03164d4 c03164d4 002154d4 2**2 CONTENTS, ALLOC, LOAD, DATA 9 .set.scrndr_set 00000024 c03164e4 c03164e4 002154e4 2**2 CONTENTS, ALLOC, LOAD, DATA 10 .set.scterm_set 0000000c c0316508 c0316508 00215508 2**2 CONTENTS, ALLOC, LOAD, DATA 11 .set.sysctl_set 0000097c c0316514 c0316514 00215514 2**2 CONTENTS, ALLOC, LOAD, DATA 12 .set.sysinit_set 00000664 c0316e90 c0316e90 00215e90 2**2 CONTENTS, ALLOC, LOAD, DATA This screen dump shows that the size of .set.sysinit_set section is 0x664 bytes, so 0x664/sizeof(void *) sysinit objects are compiled into the kernel. The other sections such as .set.sysctl_set represent other linker sets. By defining a variable of type struct linker_set the content of .set.sysinit_set section will be collected into that variable: sys/kern/init_main.c: extern struct linker_set sysinit_set; /* XXX */ The struct linker_set is defined as follows: /usr/include/linker_set.h: struct linker_set { int ls_length; void *ls_items[1]; /* really ls_length of them, trailing NULL */ }; The first node will be equal to the number of a sysinit objects, and the second node will be a NULL-terminated array of pointers to them. Returning to the mi_startup() discussion, it is must be clear now, how the sysinit objects are being organized. The mi_startup() function sorts them and calls each. The very last object is the system scheduler: /usr/include/sys/kernel.h: enum sysinit_sub_id { SI_SUB_DUMMY = 0x0000000, /* not executed; for linker*/ SI_SUB_DONE = 0x0000001, /* processed*/ SI_SUB_CONSOLE = 0x0800000, /* console*/ SI_SUB_COPYRIGHT = 0x0800001, /* first use of console*/ ... SI_SUB_RUN_SCHEDULER = 0xfffffff /* scheduler: no return*/ }; The system scheduler sysinit object is defined in the file sys/vm/vm_glue.c, and the entry point for that object is scheduler(). That function is actually an infinite loop, and it represents a process with PID 0, the swapper process. The proc0 structure, mentioned before, is used to describe it. The first user process, called init, is created by the sysinit object init: sys/kern/init_main.c: static void create_init(const void *udata __unused) { int error; int s; s = splhigh(); error = fork1(&proc0, RFFDG | RFPROC, &initproc); if (error) panic("cannot fork init: %d\n", error); initproc->p_flag |= P_INMEM | P_SYSTEM; cpu_set_fork_handler(initproc, start_init, NULL); remrunqueue(initproc); splx(s); } SYSINIT(init,SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL) The create_init() allocates a new process by calling fork1(), but does not mark it runnable. When this new process is scheduled for execution by the scheduler, the start_init() will be called. That function is defined in init_main.c. It tries to load and exec the init binary, probing /sbin/init first, then /sbin/oinit, /sbin/init.bak, and finally /stand/sysinstall: sys/kern/init_main.c: static char init_path[MAXPATHLEN] = #ifdef INIT_PATH __XSTRING(INIT_PATH); #else "/sbin/init:/sbin/oinit:/sbin/init.bak:/stand/sysinstall"; #endif diff --git a/en_US.ISO8859-1/books/arch-handbook/jail/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/jail/chapter.sgml index f792cbd338..5bf43c7833 100644 --- a/en_US.ISO8859-1/books/arch-handbook/jail/chapter.sgml +++ b/en_US.ISO8859-1/books/arch-handbook/jail/chapter.sgml @@ -1,597 +1,597 @@ Evan Sarmiento
evms@cs.bu.edu
2001 Evan Sarmiento
The Jail Subsystem On most UNIX systems, root has omnipotent power. This promotes insecurity. If an attacker were to gain root on a system, he would have every function at his fingertips. In FreeBSD there are sysctls which dilute the power of root, in order to minimize the damage caused by an attacker. Specifically, one of these functions is called secure levels. Similarly, another function which is present from FreeBSD 4.0 and onward, is a utility called &man.jail.8;. Jail chroots an environment and sets certain restrictions on processes which are forked from within. For example, a jailed process cannot affect processes outside of the jail, utilize certain system calls, or inflict any damage on the main computer. Jail is becoming the new security model. People are running potentially vulnerable servers such as Apache, BIND, and sendmail within jails, so that if an attacker gains root within the Jail, it is only an annoyance, and not a devastation. This article focuses on the internals (source code) of Jail. It will also suggest improvements upon the jail code base which are already being worked on. If you are looking for a how-to on setting up a Jail, I suggest you look at my other article in Sys Admin Magazine, May 2001, entitled "Securing FreeBSD using Jail." Architecture Jail consists of two realms: the user-space program, jail, and the code implemented within the kernel: the jail() system call and associated restrictions. I will be discussing the user-space program and then how jail is implemented within the kernel. Userland code The source for the user-land jail is located in /usr/src/usr.sbin/jail, consisting of one file, jail.c. The program takes these arguments: the path of the jail, hostname, ip address, and the command to be executed. Data Structures In jail.c, the first thing I would note is the declaration of an important structure struct jail j; which was included from - /usr/include/sys/jail.h. + /usr/include/sys/jail.h. The definition of the jail structure is: /usr/include/sys/jail.h: struct jail { u_int32_t version; char *path; char *hostname; u_int32_t ip_number; }; As you can see, there is an entry for each of the arguments passed to the jail program, and indeed, they are set during it's execution. /usr/src/usr.sbin/jail.c j.version = 0; j.path = argv[1]; j.hostname = argv[2]; Networking One of the arguments passed to the Jail program is an IP address with which the jail can be accessed over the network. Jail translates the ip address given into network byte order and then stores it in j (the jail structure). /usr/src/usr.sbin/jail/jail.c: struct in.addr in; ... i = inet.aton(argv[3], ); ... j.ip_number = ntohl(in.s.addr); The inet_aton3 function "interprets the specified character string as an Internet address, placing the address into the structure provided." The ip number node in the jail structure is set only when the ip address placed onto the in structure by inet aton is translated into network byte order by ntohl(). Jailing The Process Finally, the userland program jails the process, and executes the command specified. Jail now becomes an imprisoned process itself and forks a child process which - then executes the command given using &man.execv.3; + then executes the command given using &man.execv.3; /usr/src/sys/usr.sbin/jail/jail.c i = jail(); ... i = execv(argv[4], argv + 4); As you can see, the jail function is being called, and its argument is the jail structure which has been filled with the arguments given to the program. Finally, the program you specify is executed. I will now discuss how Jail is implemented within the kernel. Kernel Space We will now be looking at the file /usr/src/sys/kern/kern_jail.c. This is the file where the jail system call, appropriate sysctls, and networking functions are defined. sysctls In kern_jail.c, the following sysctls are defined: /usr/src/sys/kern/kern_jail.c: int jail_set_hostname_allowed = 1; SYSCTL_INT(_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW, _set_hostname_allowed, 0, "Processes in jail can set their hostnames"); int jail_socket_unixiproute_only = 1; SYSCTL_INT(_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW, _socket_unixiproute_only, 0, "Processes in jail are limited to creating UNIX/IPv4/route sockets only "); int jail_sysvipc_allowed = 0; SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW, _sysvipc_allowed, 0, "Processes in jail can use System V IPC primitives"); Each of these sysctls can be accessed by the user through the sysctl program. Throughout the kernel, these specific sysctls are recognized by their name. For example, the name of the first sysctl is jail.set.hostname.allowed. &man.jail.2; system call Like all system calls, the &man.jail.2; system call takes two arguments, struct proc *p and struct jail_args *uap. p is a pointer to a proc structure which describes the calling process. In this context, uap is a pointer to a structure which specifies the arguments given to &man.jail.2; from the userland program jail.c. When I described the userland program before, you saw that the &man.jail.2; system call was given a jail structure as its own argument. /usr/src/sys/kern/kern_jail.c: int jail(p, uap) struct proc *p; struct jail_args /* { syscallarg(struct jail *) jail; } */ *uap; Therefore, uap->jail would access the jail structure which was passed to the system call. Next, the system call copies the jail structure into kernel space using the copyin() function. copyin() takes three arguments: the data which is to be copied into kernel space, uap->jail, where to store it, j and the size of the storage. The jail structure uap->jail is copied into kernel space and stored in another jail structure, j. /usr/src/sys/kern/kern_jail.c: error = copyin(uap->jail, , sizeof j); There is another important structure defined in jail.h. It is the prison structure (pr). The prison structure is used exclusively within kernel space. The &man.jail.2; system call copies everything from the jail structure onto the prison structure. Here is the definition of the prison structure. /usr/include/sys/jail.h: struct prison { int pr_ref; char pr_host[MAXHOSTNAMELEN]; u_int32_t pr_ip; void *pr_linux; }; The jail() system call then allocates memory for a pointer to a prison structure and copies data between the two structures. /usr/src/sys/kern/kern_jail.c: MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK); bzero((caddr_t)pr, sizeof *pr); error = copyinstr(j.hostname, pr_host]]>, sizeof pr->pr_host, 0); if (error) goto bail; Finally, the jail system call chroots the path specified. The chroot function is given two arguments. The first is p, which represents the calling process, the second is a pointer to the structure chroot args. The structure chroot args contains the path which is to be chrooted. As you can see, the path specified in the jail structure is copied to the chroot args structure and used. /usr/src/sys/kern/kern_jail.c: ca.path = j.path; error = chroot(p, ); These next three lines in the source are very important, as they specify how the kernel recognizes a process as jailed. Each process on a Unix system is described by its own proc structure. You can see the whole proc structure in /usr/include/sys/proc.h. For example, the p argument in any system call is actually a pointer to that process' proc structure, as stated before. The proc structure contains nodes which can describe the owner's identity (p_cred), the process resource limits (p_limit), and so on. In the definition of the process structure, there is a pointer to a prison structure. (p_prison). /usr/include/sys/proc.h: struct proc { ... struct prison *p_prison; ... }; In kern_jail.c, the function then copies the pr structure, which is filled with all the information from the original jail structure, over to the p->p_prison structure. It then does a bitwise OR of p->p_flag with the constant P_JAILED, meaning that the calling process is now recognized as jailed. The parent process of each process, forked within the jail, is the program jail itself, as it calls the &man.jail.2; system call. When the program is executed through execve, it inherits the properties of its parents proc structure, therefore it has the p->p_flag set, and the p->p_prison structure is filled. /usr/src/sys/kern/kern_jail.c p->p.prison = pr; p->p.flag |= P.JAILED; When a process is forked from a parent process, the &man.fork.2; system call deals differently with imprisoned processes. In the fork system call, there are two pointers to a proc structure p1 and p2. p1 points to the parent's proc structure and p2 points to the child's unfilled proc structure. After copying all relevant data between the structures, &man.fork.2; checks if the structure p->p_prison is filled on p2. If it is, it increments the pr.ref by one, and sets the p_flag to one on the child process. /usr/src/sys/kern/kern_fork.c: if (p2->p_prison) { p2->p_prison->pr_ref++; p2->p_flag |= P_JAILED; } Restrictions Throughout the kernel there are access restrictions relating to jailed processes. Usually, these restrictions only check if the process is jailed, and if so, returns an error. For example: if (p->p_prison) return EPERM; SysV IPC System V IPC is based on messages. Processes can send each other these messages which tell them how to act. The functions which deal with messages are: msgsys, msgctl, msgget, msgsend and msgrcv. Earlier, I mentioned that there were certain sysctls you could turn on or off in order to affect the behavior of Jail. One of these sysctls was jail_sysvipc_allowed. On most systems, this sysctl is set to 0. If it were set to 1, it would defeat the whole purpose of having a jail; privleged users from within the jail would be able to affect processes outside of the environment. The difference between a message and a signal is that the message only consists of the signal number. /usr/src/sys/kern/sysv_msg.c: &man.msgget.3;: msgget returns (and possibly creates) a message descriptor that designates a message queue for use in other system calls. &man.msgctl.3;: Using this function, a process can query the status of a message descriptor. &man.msgsnd.3;: msgsnd sends a message to a process. &man.msgrcv.3;: a process receives messages using this function In each of these system calls, there is this conditional: /usr/src/sys/kern/sysv msg.c: if (!jail.sysvipc.allowed && p->p_prison != NULL) return (ENOSYS); Semaphore system calls allow processes to synchronize execution by doing a set of operations atomically on a set of semaphores. Basically semaphores provide another way for processes lock resources. However, process waiting on a semaphore, that is being used, will sleep until the resources are relinquished. The following semaphore system calls are blocked inside a jail: semsys, semget, semctl and semop. /usr/src/sys/kern/sysv_sem.c: &man.semctl.2;(id, num, cmd, arg): Semctl does the specified cmd on the semaphore queue indicated by id. &man.semget.2;(key, nsems, flag): Semget creates an array of semaphores, corresponding to key. Key and flag take on the same meaning as they do in msgget. &man.semop.2;(id, ops, num): Semop does the set of semaphore operations in the array of structures ops, to the set of semaphores identified by id. System V IPC allows for processes to share memory. Processes can communicate directly with each other by sharing parts of their virtual address space and then reading and writing data stored in the shared memory. These system calls are blocked within a jailed environment: shmdt, shmat, oshmctl, shmctl, shmget, and shmsys. /usr/src/sys/kern/sysv shm.c: &man.shmctl.2;(id, cmd, buf): shmctl does various control operations on the shared memory region identified by id. &man.shmget.2;(key, size, flag): shmget accesses or creates a shared memory region of size bytes. &man.shmat.2;(id, addr, flag): shmat attaches a shared memory region identified by id to the address space of a process. &man.shmdt.2;(addr): shmdt detaches the shared memory region previously attached at addr. Sockets Jail treats the &man.socket.2; system call and related lower-level socket functions in a special manner. In order to determine whether a certain socket is allowed to be created, it first checks to see if the sysctl jail.socket.unixiproute.only is set. If set, sockets are only allowed to be created if the family specified is either PF_LOCAL, PF_INET or PF_ROUTE. Otherwise, it returns an error. /usr/src/sys/kern/uipc_socket.c: int socreate(dom, aso, type, proto, p) ... register struct protosw *prp; ... { if (p->p_prison && jail_socket_unixiproute_only && prp->pr_domain->dom_family != PR_LOCAL && prp->pr_domain->dom_family != PF_INET && prp->pr_domain->dom_family != PF_ROUTE) return (EPROTONOSUPPORT); ... } Berkeley Packet Filter The Berkeley Packet Filter provides a raw interface to data link layers in a protocol independent fashion. The function bpfopen() opens an Ethernet device. There is a conditional which disallows any jailed processes from accessing this function. /usr/src/sys/net/bpf.c: static int bpfopen(dev, flags, fmt, p) ... { if (p->p_prison) return (EPERM); ... } Protocols There are certain protocols which are very common, such as TCP, UDP, IP and ICMP. IP and ICMP are on the same level: the network layer 2. There are certain precautions which are taken in order to prevent a jailed process from binding a protocol to a certain port only if the nam parameter is set. nam is a pointer to a sockaddr structure, which describes the address on which to bind the service. A more exact definition is that sockaddr "may be used as a template for reffering to the identifying tag and length of each address"[2]. In the function in pcbbind, sin is a pointer to a sockaddr.in structure, which contains the port, address, length and domain family of the socket which is to be bound. Basically, this disallows any processes from jail to be able to specify the domain family. /usr/src/sys/kern/netinet/in_pcb.c: int in.pcbbind(int, nam, p) ... struct sockaddr *nam; struct proc *p; { ... struct sockaddr.in *sin; ... if (nam) { sin = (struct sockaddr.in *)nam; ... if (sin->sin_addr.s_addr != INADDR_ANY) if (prison.ip(p, 0, ->sin.addr.s_addr)) return (EINVAL); .... } ... } You might be wondering what function prison_ip() does. prison.ip is given three arguments, the current process (represented by p), any flags, and an ip address. It returns 1 if the ip address belongs to a jail or 0 if it does not. As you can see from the code, if it is indeed an ip address belonging to a jail, the protcol is not allowed to bind to a certain port. /usr/src/sys/kern/kern_jail.c: int prison_ip(struct proc *p, int flag, u_int32_t *ip) { u_int32_t tmp; if (!p->p_prison) return (0); if (flag) tmp = *ip; else tmp = ntohl (*ip); if (tmp == INADDR_ANY) { if (flag) *ip = p->p_prison->pr_ip; else *ip = htonl(p->p_prison->pr_ip); return (0); } if (p->p_prison->pr_ip != tmp) return (1); return (0); } Jailed users are not allowed to bind services to an ip which does not belong to the jail. The restriction is also written within the function in_pcbbind: /usr/src/sys/net inet/in_pcb.c if (nam) { ... lport = sin->sin.port; ... if (lport) { ... if (p && p->p_prison) prison = 1; if (prison && prison_ip(p, 0, ->sin_addr.s_addr)) return (EADDRNOTAVAIL); Filesystem Even root users within the jail are not allowed to set any file flags, such as immutable, append, and no unlink flags, if the securelevel is greater than 0. /usr/src/sys/ufs/ufs/ufs_vnops.c: int ufs.setattr(ap) ... { if ((cred->cr.uid == 0) && (p->prison == NULL)) { if ((ip->i_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) && securelevel > 0) return (EPERM); }
diff --git a/en_US.ISO8859-1/books/arch-handbook/mac/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/mac/chapter.sgml index ccd16eaf70..1946e991c0 100644 --- a/en_US.ISO8859-1/books/arch-handbook/mac/chapter.sgml +++ b/en_US.ISO8859-1/books/arch-handbook/mac/chapter.sgml @@ -1,7819 +1,7821 @@ Chris Costello TrustedBSD Project
chris@FreeBSD.org
Robert Watson TrustedBSD Project
rwatson@FreeBSD.org
The TrustedBSD MAC Framework MAC Documentation Copyright This documentation was developed for the FreeBSD Project by Chris Costello at Safeport Network Services and Network Associates Laboratories, the Security Research Division of Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 (CBOSS), as part of the DARPA CHATS research program. Redistribution and use in source (SGML DocBook) and 'compiled' forms (SGML, HTML, PDF, PostScript, RTF and so forth) with or without modification, are permitted provided that the following conditions are met: Redistributions of source code (SGML DocBook) must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified. Redistributions in compiled form (transformed to other DTDs, converted to PDF, PostScript, RTF and other formats) 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 DOCUMENTATION IS PROVIDED BY THE NETWORKS ASSOCIATES TECHNOLOGY, INC "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 NETWORKS ASSOCIATES TECHNOLOGY, INC 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 DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Synopsis FreeBSD includes experimental support for several mandatory access control policies, as well as a framework for kernel security extensibility, the TrustedBSD MAC Framework. The MAC Framework provides a pluggable access control framework, permitting new security policies to be easily linked into the kernel, loaded at boot, or loaded dynamically at run-time. The framework provides a variety of features to make it easier to implement new policies, including the ability to easily tag security labels (such as confidentiality information) onto system objects. This chapter introduces the MAC policy framework and provides documentation for a sample MAC policy module. Introduction The TrustedBSD MAC framework provides a mechanism to allow the compile-time or run-time extension of the kernel access control model. New system policies may be implemented as kernel modules and linked to the kernel; if multiple policy modules are present, their results will be composed. The MAC Framework provides a variety of access control infrastructure services to assist policy writers, including support for transient and persistent policy-agnostic object security labels. This support is currently considered experimental. Policy Background Mandatory Access Control (MAC), refers to a set of access control policies that are mandatorily enforced on users by the operating system. MAC policies may be contrasted with Discretionary Access Control (DAC) protections, by which non-administrative users may (at their discretion) protect objects. In traditional UNIX systems, DAC protections include file permissions and access control lists; MAC protections include process controls preventing inter-user debugging and firewalls. A variety of MAC policies have been formulated by operating system designers and security researches, including the Multi-Level Security (MLS) confidentiality policy, the Biba integrity policy, Role-Based Access Control (RBAC), and Type Enforcement (TE). Each model bases decisions on a variety of factors, including user identity, role, and security clearance, as well as security labels on objects representing concepts such as data sensitivity and integrity. The TrustedBSD MAC Framework is capable of supporting policy modules that implement all of these policies, as well as a broad class of system hardening policies. In addition, despite the name, the MAC Framework can also be used to implement purely discretionary policies, as policy modules are given substantial flexibility in how they authorize protections. MAC Framework Kernel Architecture The TrustedBSD MAC Framework permits kernel modules to extend the operating system security policy, as well as providing infrastructure functionality required by many access control modules. If multiple policies are simultaneously loaded, the MAC Framework will usefully (for some definition of useful) compose the results of the policies. Kernel Elements The MAC Framework contains a number of kernel elements: Framework management interfaces Concurrency and synchronization primitives. Policy registration Extensible security label for kernel objects Policy entry point composition operators Label management primitives Entry point API invoked by kernel services Entry point API to policy modules Entry points implementations (policy life cycle, object life cycle/label management, access control checks). Policy-agnostic label-management system calls mac_syscall() multiplex system call Various security policies implemented as MAC policy modules Management Interfaces The TrustedBSD MAC Framework may be directly managed using sysctls, loader tunables, and system calls. In most cases, sysctls and loader tunables modify the same parameters, and control behavior such as enforcement of protections relating to various kernel subsystems. In addition, if MAC debugging support is compiled into the kernel, a variety of counters will be maintained tracking label allocation. In most cases, it is advised that per-subsystem enforcement controls not be used to control policy behavior in production environments, as they broadly impact the operation of all active policies. Instead, per-policy controls should be preferred to ensure proper policy operation. Loading and unloading of policy modules is performed using the system module management system calls and other system interfaces, including loader variables. Concurrency and Synchronization As the set of active policies may change at run-time, and the invocation of entry points is non-atomic, synchronization is required to prevent unloading or loading of new policies while an entry point invocation is progress, freezing the list of policies for the duration. This is accomplished by means of a Framework busy count. Whenever an entry point is entered, the busy count is incremented; whenever it is exited, the busy count is decremented. While the busy count is elevated, policy list changes are not permitted, and threads attempting to modify the policy list will sleep until the list is not busy. The busy count is protected by a mutex, and a condition variable is used to wake up sleepers waiting on policy list modifications. Various optimizations are used to reduce the overhead of the busy count, including avoiding the full cost of incrementing and decrementing if the list is empty or contains only static entries (policies that are loaded before the system starts, and cannot be unloaded). Policy Registration The MAC Framework maintains two lists of active policies: a static list, and a dynamic list. The lists differ only with regards to their locking semantics: an elevated reference count is not required to make use of the static list. When kernel modules containing MAC Framework policies are loaded, the policy module will use SYSINIT to invoke a registration function; when a policy module is unloaded, SYSINIT will likewise invoke a de-registration function. Registration may fail if a policy module is loaded more than once, if insufficient resources are available for the registration (for example, the policy might require labeling and insufficient labeling state might be available), or other policy prerequisites might not be met (some policies may only be loaded prior to boot). Likewise, de-registration may fail if a policy refuses an unload. Entry Points Kernel services interact with the MAC Framework in two ways: they invoke a series of APIs to notify the framework of relevant events, and they a policy-agnostic label structure in security-relevant objects. This label structure is maintained by the MAC Framework via label management entry points, and permits the Framework to offer a labeling service to policy modules through relatively non-invasive changes to the kernel subsystem maintaining the object. For example, label structures have been added to processes, process credentials, sockets, pipes, vnodes, Mbufs, network interfaces, IP reassembly queues, and a variety of other security-relevant structures. Kernel services also invoke the MAC Framework when they perform important security decisions, permitting policy modules to augment those decisions based on their own criteria (possibly including data stored in security labels). Policy Composition When more than one policy module is loaded into the kernel at a time, the results of the policy modules will be composed by the framework using a composition operator. This operator is currently hard-coded, and requires that all active policies must approve a request for it to occur. As policies may return a variety of error conditions (success, access denied, object doesn't exist, ...), a precedence operator selects the resulting error from the set of errors returned by policies. While it is not guaranteed that the resulting composition will be useful or secure, we've found that it is for many useful selections of policies. Labeling Support As many interesting access control extensions rely on security labels on objects, the MAC Framework provides a set of policy-agnostic label management system calls covering a variety of user-exposed objects. Common label types include partition identifiers, sensitivity labels, integrity labels, compartments, domains, roles, and types. Policy modules participate in the internalization and externalization of string-based labels provides by user applications, and can expose multiple label elements to applications if desired. In-memory labels are stored in struct label, which consists of a fixed-length array of unions, each holding a void * pointer and a long. Policies registering for label storage will be assigned a "slot" identifier, which may be used to dereference the label storage. The semantics of the storage are left entirely up to the policy module: modules are provided with a variety of entry points associated with the kernel object life cycle, including initialization, association/creation, and destruction. Using these interfaces, it is possible to implement reference counting and other storage mechanisms. Direct access to the kernel object is generally not required by policy modules to retrieve a label, as the MAC Framework generally passes both a pointer to the object and a direct pointer to the object's label into entry points. Initialization entry points frequently include a blocking disposition flag indicating whether or not an initialization is permitted to block; if blocking is not permitted, a failure may be returned to cancel allocation of the label. This may occur, for example, in the network stack during interrupt handling, where blocking is not permitted. Due to the performance cost of maintaining labels on in-flight network packets (Mbufs), policies must specifically declare a requirement that Mbuf labels be allocated. Dynamically loaded policies making use of labels must be able to handle the case where their init function has not been called on an object, as objects may already exist when the policy is loaded. In the case of file system labels, special support is provided for the persistent storage of security labels in extended attributes. Where available, EA transactions are used to permit consistent compound updates of security labels on vnodes. Currently, if a labeled policy permits dynamic unloading, its state slot cannot be reclaimed. System Calls The MAC Framework implements a number of system calls: most of these calls support the policy-agnostic label retrieval and manipulation APIs exposed to user applications. The label management calls accept a label description structure, struct mac, which contains a series of MAC label elements. Each element contains a character string name, and character string value. Each policy will be given the chance to claim a particular element name, permitting policies to expose multiple independent elements if desired. Policy modules perform the internalization and externalization between kernel labels and user-provided labels via entry points, permitting a variety of semantics. Label management system calls are generally wrapped by user library functions to perform memory allocation and error handling. In addition, mac_syscall() permits policy modules to create new system calls without allocating system calls. mac_execve() permits an atomic process credential label change when executing a new image. MAC Policy Architecture Security policies are either linked directly into the kernel, or compiled into loadable kernel modules that may be loaded at boot, or dynamically using the module loading system calls at runtime. Policy modules interact with the system through a set of declared entry points, providing access to a stream of system events and permitting the policy to influence access control decisions. Each policy contains a number of elements: Optional configuration parameters for policy. Centralized implementation of the policy logic and parameters. Optional implementation of policy life cycle events, such as initialization and destruction. Optional support for initializing, maintaining, and destroying labels on selected kernel objects. Optional support for user process inspection and modification of labels on selected objects. Implementation of selected access control entry points that are of interest to the policy. Declaration of policy identity, module entry points, and policy properties. Policy Declaration Modules may be declared using the MAC_POLICY_SET() macro, which names the policy, provides a reference to the MAC entry point vector, provides load-time flags determining how the policy framework should handle the policy, and optionally requests the allocation of label state by the framework. static struct mac_policy_ops mac_policy_ops = { .mpo_destroy = mac_policy_destroy, .mpo_init = mac_policy_init, .mpo_init_bpfdesc_label = mac_policy_init_bpfdesc_label, .mpo_init_cred_label = mac_policy_init_label, /* ... */ .mpo_check_vnode_setutimes = mac_policy_check_vnode_setutimes, .mpo_check_vnode_stat = mac_policy_check_vnode_stat, .mpo_check_vnode_write = mac_policy_check_vnode_write, }; The MAC policy entry point vector, mac_policy_ops in this example, associates functions defined in the module with specific entry points. A complete listing of available entry points and their prototypes may be found in the MAC entry point reference section. Of specific interest during module registration are the .mpo_destroy and .mpo_init entry points. .mpo_init will be invoked once a policy is successfully registered with the module framework but prior to any other entry points becoming active. This permits the policy to perform any policy-specific allocation and initialization, such as initialization of any data or locks. .mpo_destroy will be invoked when a policy module is unloaded to permit releasing of any allocated memory and destruction of locks. Currently, these two entry points are invoked with the MAC policy list mutex held to prevent any other entry points from being invoked: this will be changed, but in the mean time, policies should be careful about what kernel primitives they invoke so as to avoid lock ordering or sleeping problems. The policy declaration's module name field exists so that the module may be uniquely identified for the purposes of module dependencies. An appropriate string should be selected. The full string name of the policy is displayed to the user via the kernel log during load and unload events, and also exported when providing status information to userland processes. Policy Flags The policy declaration flags field permits the module to provide the framework with information about its capabilities at the time the module is loaded. Currently, three flags are defined: MPC_LOADTIME_FLAG_UNLOADOK This flag indicates that the policy module may be unloaded. If this flag is not provided, then the policy framework will reject requests to unload the module. This flag might be used by modules that allocate label state and are unable to free that state at runtime. MPC_LOADTIME_FLAG_NOTLATE This flag indicates that the policy module must be loaded and initialized early in the boot process. If the flag is specified, attempts to register the module following boot will be rejected. The flag may be used by policies that require pervasive labeling of all system objects, and cannot handle objects that have not been properly initialized by the policy. MPC_LOADTIME_FLAG_LABELMBUFS This flag indicates that the policy module requires labeling of Mbufs, and that memory should always be allocated for the storage of Mbuf labels. By default, the MAC Framework will not allocate label storage for Mbufs unless at least one loaded policy has this flag set. This measurably improves network performance when policies do not require Mbuf labeling. A kernel option, MAC_ALWAYS_LABEL_MBUF, exists to force the MAC Framework to allocate Mbuf label storage regardless of the setting of this flag, and may be useful in some environments. Policies using the MPC_LOADTIME_FLAG_LABELMBUFS without the MPC_LOADTIME_FLAG_NOTLATE flag set must be able to correctly handle NULL Mbuf label pointers passed into entry points. This is necessary as in-flight Mbufs without label storage may persist after a policy enabling Mbuf labeling has been loaded. If a policy is loaded before the network subsystem is active (i.e., the policy is not being loaded late), then all Mbufs are guaranteed to have label storage. Policy Entry Points Four classes of entry points are offered to policies registered with the framework: entry points associated with the registration and management of policies, entry points denoting initialization, creation, destruction, and other life cycle events for kernel objects, events associated with access control decisions that the policy module may influence, and calls associated with the management of labels on objects. In addition, a mac_syscall() entry point is provided so that policies may extend the kernel interface without registering new system calls. Policy module writers should be aware of the kernel locking strategy, as well as what object locks are available during which entry points. Writers should attempt to avoid deadlock scenarios by avoiding grabbing non-leaf locks inside of entry points, and also follow the locking protocol for object access and modification. In particular, writers should be aware that while necessary locks to access objects and their labels are generally held, sufficient locks to modify an object or its label may not be present for all entry points. Locking information for arguments is documented in the MAC framework entry point document. Policy entry points will pass a reference to the object label along with the object itself. This permits labeled policies to be unaware of the internals of the object yet still make decisions based on the label. The exception to this is the process credential, which is assumed to be understood by policies as a first class security object in the kernel. Policies that do not implement labels on kernel objects will be passed NULL pointers for label arguments to entry points. MAC Policy Entry Point Reference General-Purpose Module Entry Points <function>&mac.mpo;_init</function void &mac.mpo;_init struct mac_policy_conf *conf &mac.thead; conf MAC policy definition Policy load event. The policy list mutex is held, so caution should be applied. <function>&mac.mpo;_destroy</function> void &mac.mpo;_destroy struct mac_policy_conf *conf &mac.thead; conf MAC policy definition Policy load event. The policy list mutex is held, so caution should be applied. <function>&mac.mpo;_syscall</function> int &mac.mpo;_syscall struct thread *td int call void *arg &mac.thead; td Calling thread call Syscall number arg Pointer to syscall arguments This entry point provides a policy-multiplexed system call so that policies may provide additional services to user processes without registering specific system calls. The policy name provided during registration is used to demux calls from userland, and the arguments will be forwarded to this entry point. When implementing new services, security modules should be sure to invoke appropriate access control checks from the MAC framework as needed. For example, if a policy implements an augmented signal functionality, it should call the necessary signal access control checks to invoke the MAC framework and other registered policies. Modules must currently perform the copyin() of the syscall data on their own. <function>&mac.mpo;_thread_userret</function> void &mac.mpo;_thread_userret struct thread *td &mac.thead; td Returning thread This entry point permits policy modules to perform MAC-related events when a thread returns to user space. This is required for policies that have floating process labels, as it's not always possible to acquire the process lock at arbitrary points in the stack during system call processing; process labels might represent traditional authentication data, process history information, or other data. Label Operations <function>&mac.mpo;_init_bpfdesc_label</function> void &mac.mpo;_init_bpfdesc_label struct label *label &mac.thead; label New label to apply Initialize the label on a newly instantiated bpfdesc (BPF descriptor) <function>&mac.mpo;_init_cred_label</function> void &mac.mpo;_init_cred_label struct label *label &mac.thead; label New label to initialize Initialize the label for a newly instantiated user credential. <function>&mac.mpo;_init_devfsdirent_label</function> void &mac.mpo;_init_devfsdirent_label struct label *label &mac.thead; label New label to apply Initialize the label on a newly instantiated devfs entry. <function>&mac.mpo;_init_ifnet_label</function> void &mac.mpo;_init_ifnet_label struct label *label &mac.thead; label New label to apply Initialize the label on a newly instantiated network interface. <function>&mac.mpo;_init_ipq_label</function> void &mac.mpo;_init_ipq_label struct label *label int flag &mac.thead; label New label to apply flag Blocking/non-blocking &man.malloc.9;; see below Initialize the label on a newly instantiated IP fragment reassembly queue. The flag field may be one of M_WAITOK and M_NOWAIT, and should be employed to avoid performing a blocking &man.malloc.9; during this initialization call. IP fragment reassembly queue allocation frequently occurs in performance sensitive environments, and the implementation should be careful to avoid blocking or long-lived operations. This entry point is permitted to fail resulting in the failure to allocate the IP fragment reassembly queue. <function>&mac.mpo;_init_mbuf_label</function> void &mac.mpo;_init_mbuf_label int flag struct label *label &mac.thead; flag Blocking/non-blocking &man.malloc.9;; see below label Policy label to initialize + Initialize the label on a newly instantiated mbuf packet header (mbuf). The flag field may be one of M_WAITOK and M_NOWAIT, and should be employed to avoid performing a blocking &man.malloc.9; during this initialization call. Mbuf allocation frequently occurs in performance sensitive environments, and the implementation should be careful to avoid blocking or long-lived operations. This entry point is permitted to fail resulting in the failure to allocate the mbuf header. <function>&mac.mpo;_init_mount_label</function> void &mac.mpo;_init_mount_label struct label *mntlabel struct label *fslabel &mac.thead; mntlabel Policy label to be initialized for the mount itself fslabel Policy label to be initialized for the file system Initialize the labels on a newly instantiated mount point. <function>&mac.mpo;_init_mount_fs_label</function> void &mac.mpo;_init_mount_fs_label struct label *label &mac.thead; label Label to be initialized Initialize the label on a newly mounted file system. <function>&mac.mpo;_init_pipe_label</function> void &mac.mpo;_init_pipe_label struct label*label &mac.thead; label Label to be filled in + Initialize a label for a newly instantiated pipe. <function>&mac.mpo;_init_socket_label</function> void &mac.mpo;_init_socket_label struct label *label int flag &mac.thead; label New label to initialize flag &man.malloc.9; flags Initialize a label for a newly instantiated socket. <function>&mac.mpo;_init_socket_peer_label</function> void &mac.mpo;_init_socket_peer_label struct label *label int flag &mac.thead; label New label to initialize flag &man.malloc.9; flags Initialize the peer label for a newly instantiated socket. <function>&mac.mpo;_init_proc_label</function> void &mac.mpo;_init_proc_label struct label *label &mac.thead; label New label to initialize Initialize the label for a newly instantiated process. <function>&mac.mpo;_init_vnode_label</function> void &mac.mpo;_init_vnode_label struct label *label &mac.thead; label New label to initialize Initialize the label on a newly instantiated vnode. <function>&mac.mpo;_destroy_bpfdesc_label</function> void &mac.mpo;_destroy_bpfdesc_label struct label *label &mac.thead; label bpfdesc label Destroy the label on a BPF descriptor. In this entry point a policy should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_cred_label</function> void &mac.mpo;_destroy_cred_label struct label *label &mac.thead; label Label being destroyed Destroy the label on a credential. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_devfsdirent_label</function> void &mac.mpo;_destroy_devfsdirent_label struct label *label &mac.thead; label Label being destroyed Destroy the label on a devfs entry. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_ifnet_label</function> void &mac.mpo;_destroy_ifnet_label struct label *label &mac.thead; label Label being destroyed Destroy the label on a removed interface. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_ipq_label</function> void &mac.mpo;_destroy_ipq_label struct label *label &mac.thead; label Label being destroyed Destroy the label on an IP fragment queue. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_mbuf_label</function> void &mac.mpo;_destroy_mbuf_label struct label *label &mac.thead; label Label being destroyed Destroy the label on an mbuf header. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_mount_label</function> void &mac.mpo;_destroy_mount_label struct label *label &mac.thead; label Mount point label being destroyed Destroy the labels on a mount point. In this entry point, a policy module should free the internal storage associated with mntlabel so that they may be destroyed. <function>&mac.mpo;_destroy_mount_label</function> void &mac.mpo;_destroy_mount_label struct label *mntlabel struct label *fslabel &mac.thead; mntlabel Mount point label being destroyed fslabel - File system label being destroyed> + File system label being destroyed> Destroy the labels on a mount point. In this entry point, a policy module should free the internal storage associated with mntlabel and fslabel so that they may be destroyed. <function>&mac.mpo;_destroy_socket_label</function> void &mac.mpo;_destroy_socket_label struct label *label &mac.thead; label Socket label being destroyed Destroy the label on a socket. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_socket_peer_label</function> void &mac.mpo;_destroy_socket_peer_label struct label *peerlabel &mac.thead; peerlabel Socket peer label being destroyed Destroy the peer label on a socket. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_pipe_label</function> void &mac.mpo;_destroy_pipe_label struct label *label &mac.thead; label Pipe label Destroy the label on a pipe. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_proc_label</function> void &mac.mpo;_destroy_proc_label struct label *label &mac.thead; label Process label Destroy the label on a process. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_vnode_label</function> void &mac.mpo;_destroy_vnode_label struct label *label &mac.thead; label Process label Destroy the label on a vnode. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_copy_mbuf_label</function> void &mac.mpo;_copy_mbuf_label struct label *src struct label *dest &mac.thead; src Source label dest Destination label Copy the label information in src into dest. <function>&mac.mpo;_copy_pipe_label</function> void &mac.mpo;_copy_pipe_label struct label *src struct label *dest &mac.thead; src Source label dest Destination label Copy the label information in src into dest. <function>&mac.mpo;_copy_vnode_label</function> void &mac.mpo;_copy_vnode_label struct label *src struct label *dest &mac.thead; src Source label dest Destination label Copy the label information in src into dest. <function>&mac.mpo;_externalize_cred_label</function> int &mac.mpo;_externalize_cred_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_ifnet_label</function> int &mac.mpo;_externalize_ifnet_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_pipe_label</function> int &mac.mpo;_externalize_pipe_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_socket_label</function> int &mac.mpo;_externalize_socket_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_socket_peer_label</function> int &mac.mpo;_externalize_socket_peer_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_vnode_label</function> int &mac.mpo;_externalize_vnode_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_internalize_cred_label</function> int &mac.mpo;_internalize_cred_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_ifnet_label</function> int &mac.mpo;_internalize_ifnet_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_pipe_label</function> int &mac.mpo;_internalize_pipe_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_socket_label</function> int &mac.mpo;_internalize_socket_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_vnode_label</function> int &mac.mpo;_internalize_vnode_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; Label Events This class of entry points is used by the MAC framework to permit policies to maintain label information on kernel objects. For each labeled kernel object of interest to a MAC policy, entry points may be registered for relevant life cycle events. All objects implement initialization, creation, and destruction hooks. Some objects will also implement relabeling, allowing user processes to change the labels on objects. Some objects will also implement object-specific events, such as label events associated with IP reassembly. A typical labeled object will have the following life cycle of entry points: Label initialization o (object-specific wait) \ Label creation o \ Relabel events, o--<--. Various object-specific, | | Access control events ~-->--o \ Label destruction o Label initialization permits policies to allocate memory and set initial values for labels without context for the use of the object. The label slot allocated to a policy will be zeroed by default, so some policies may not need to perform initialization. Label creation occurs when the kernel structure is associated with an actual kernel object. For example, Mbufs may be allocated and remain unused in a pool until they are required. mbuf allocation causes label initialization on the mbuf to take place, but mbuf creation occurs when the mbuf is associated with a datagram. Typically, context will be provided for a creation event, including the circumstances of the creation, and labels of other relevant objects in the creation process. For example, when an mbuf is created from a socket, the socket and its label will be presented to registered policies in addition to the new mbuf and its label. Memory allocation in creation events is discouraged, as it may occur in performance sensitive ports of the kernel; in addition, creation calls are not permitted to fail so a failure to allocate memory cannot be reported. Object specific events do not generally fall into the other broad classes of label events, but will generally provide an opportunity to modify or update the label on an object based on additional context. For example, the label on an IP fragment reassembly queue may be updated during the MAC_UPDATE_IPQ entry point as a result of the acceptance of an additional mbuf to that queue. Access control events are discussed in detail in the following section. Label destruction permits policies to release storage or state associated with a label during its association with an object so that the kernel data structures supporting the object may be reused or released. In addition to labels associated with specific kernel objects, an additional class of labels exists: temporary labels. These labels are used to store update information submitted by user processes. These labels are initialized and destroyed as with other label types, but the creation event is MAC_INTERNALIZE, which accepts a user label to be converted to an in-kernel representation. File System Object Labeling Event Operations <function>&mac.mpo;_associate_vnode_devfs</function> void &mac.mpo;_associate_vnode_devfs struct mount *mp struct label *fslabel struct devfs_dirent *de struct label *delabel struct vnode *vp struct label *vlabel &mac.thead; mp Devfs mount point fslabel Devfs file system label (mp->mnt_fslabel) de Devfs directory entry delabel Policy label associated with de vp vnode associated with de vlabel Policy label associated with vp Fill in the label (vlabel) for a newly created devfs vnode based on the devfs directory entry passed in de and its label. <function>&mac.mpo;_associate_vnode_extattr</function> int &mac.mpo;_associate_vnode_extattr struct mount *mp struct label *fslabel struct vnode *vp struct label *vlabel &mac.thead; mp File system mount point fslabel File system label vp Vnode to label vlabel Policy label associated with vp Attempt to retrieve the label for vp from the file system extended attributes. Upon success, the value 0 is returned. Should extended attribute retrieval not be supported, an accepted fallback is to copy fslabel into vlabel. In the event of an error, an appropriate value for errno should be returned. <function>&mac.mpo;_associate_vnode_singlelabel</function> void &mac.mpo;_associate_vnode_singlelabel struct mount *mp struct label *fslabel struct vnode *vp struct label *vlabel &mac.thead; mp File system mount point fslabel File system label vp Vnode to label vlabel Policy label associated with vp On non-multilabel file systems, this entry point is called to set the policy label for vp based on the file system label, fslabel. <function>&mac.mpo;_create_devfs_device</function> void &mac.mpo;_create_devfs_device dev_t dev struct devfs_dirent *devfs_dirent struct label *label &mac.thead; dev Device corresponding with devfs_dirent devfs_dirent Devfs directory entry to be labeled. label Label for devfs_dirent to be filled in. Fill out the label on a devfs_dirent being created for the passed device. This call will be made when the device file system is mounted, regenerated, or a new device is made available. <function>&mac.mpo;_create_devfs_directory</function> void &mac.mpo;_create_devfs_directory char *dirname int dirnamelen struct devfs_dirent *devfs_dirent struct label *label &mac.thead; dirname Name of directory being created namelen Length of string dirname devfs_dirent Devfs directory entry for directory being created. Fill out the label on a devfs_dirent being created for the passed directory. This call will be made when the device file system is mounted, regenerated, or a new device requiring a specific directory hierarchy is made available. <function>&mac.mpo;_create_devfs_symlink</function> void &mac.mpo;_create_devfs_symlink struct ucred *cred struct mount *mp struct devfs_dirent *dd struct label *ddlabel struct devfs_dirent *de struct label *delabel &mac.thead; cred Subject credential mp Devfs mount point dd Link destination ddlabel Label associated with dd de Symlink entry delabel Label associated with de Fill in the label (delabel) for a newly created &man.devfs.5; symbolic link entry. <function>&mac.mpo;_create_vnode_extattr</function> int &mac.mpo;_create_vnode_extattr struct ucred *cred struct mount *mp struct label *fslabel struct vnode *dvp struct label *dlabel struct vnode *vp struct label *vlabel struct componentname *cnp &mac.thead; cred Subject credential mount File system mount point label File system label dvp Parent directory vnode dlabel Label associated with dvp vp Newly created vnode vlabel Policy label associated with vp cnp Component name for vp Write out the label for vp to the appropriate extended attribute. If the write succeeds, fill in vlabel with the label, and return 0. Otherwise, return an appropriate error. <function>&mac.mpo;_create_mount</function> void &mac.mpo;_create_mount struct ucred *cred struct mount *mp struct label *mnt struct label *fslabel &mac.thead; cred Subject credential mp Object; file system being mounted mntlabel Policy label to be filled in for mp fslabel Policy label for the file system mp mounts. Fill out the labels on the mount point being created by the passed subject credential. This call will be made when a new file system is mounted. <function>&mac.mpo;_create_root_mount</function> void &mac.mpo;_create_root_mount struct ucred *cred struct mount *mp struct label *mntlabel struct label *fslabel &mac.thead; See . Fill out the labels on the mount point being created by the passed subject credential. This call will be made when the root file system is mounted, after &mac.mpo;_create_mount;. <function>&mac.mpo;_relabel_vnode</function> void &mac.mpo;_relabel_vnode struct ucred *cred struct vnode *vp struct label *vnodelabel struct label *newlabel &mac.thead; cred Subject credential vp vnode to relabel vnodelabel Existing policy label for vp newlabel New, possibly partial label to replace vnodelabel Update the label on the passed vnode given the passed update vnode label and the passed subject credential. <function>&mac.mpo;_setlabel_vnode_extattr</function> int &mac.mpo;_setlabel_vnode_extattr struct ucred *cred struct vnode *vp struct label *vlabel struct label *intlabel &mac.thead; cred Subject credential vp Vnode for which the label is being written vlabel Policy label associated with vp intlabel Label to write out Write out the policy from intlabel to an extended attribute. This is called from vop_stdcreatevnode_ea. <function>&mac.mpo;_update_devfsdirent</function> void &mac.mpo;_update_devfsdirent struct devfs_dirent *devfs_dirent struct label *direntlabel struct vnode *vp struct label *vnodelabel &mac.thead; devfs_dirent Object; devfs directory entry direntlabel Policy label for devfs_dirent to be updated. vp Parent vnode Locked vnodelabel Policy label for vp Update the devfs_dirent label from the passed devfs vnode label. This call will be made when a devfs vnode has been successfully relabeled to commit the label change such that it lasts even if the vnode is recycled. It will also be made when when a symlink is created in devfs, following a call to mac_vnode_create_from_vnode to initialize the vnode label. IPC Object Labeling Event Operations <function>&mac.mpo;_create_mbuf_from_socket</function> void &mac.mpo;_create_mbuf_from_socket struct socket *so struct label *socketlabel struct mbuf *m struct label *mbuflabel &mac.thead; socket Socket Socket locking WIP socketlabel Policy label for socket m Object; mbuf mbuflabel Policy label to fill in for m Set the label on a newly created mbuf header from the passed socket label. This call is made when a new datagram or message is generated by the socket and stored in the passed mbuf. <function>&mac.mpo;_create_pipe</function> void &mac.mpo;_create_pipe struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Set the label on a newly created pipe from the passed subject credential. This call is made when a new pipe is created. <function>&mac.mpo;_create_socket</function> void &mac.mpo;_create_socket struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential Immutable so Object; socket to label socketlabel Label to fill in for so Set the label on a newly created socket from the passed subject credential. This call is made when a socket is created. <function>&mac.mpo;_create_socket_from_socket</function> void &mac.mpo;_create_socket_from_socket struct socket *oldsocket struct label *oldsocketlabel struct socket *newsocket struct label *newsocketlabel &mac.thead; oldsocket Listening socket oldsocketlabel Policy label associated with oldsocket newsocket New socket newsocketlabel Policy label associated with newsocketlabel Label a socket, newsocket, newly &man.accept.2;ed, based on the &man.listen.2; socket, oldsocket. <function>&mac.mpo;_relabel_pipe</function> void &mac.mpo;_relabel_pipe struct ucred *cred struct pipe *pipe struct label *oldlabel struct label *newlabel &mac.thead; cred Subject credential pipe Pipe oldlabel Current policy label associated with pipe newlabel Policy label update to apply to pipe Apply a new label, newlabel, to pipe. <function>&mac.mpo;_relabel_socket</function> void &mac.mpo;_relabel_socket struct ucred *cred struct socket *so struct label *oldlabel struct label *newlabel &mac.thead; cred Subject credential Immutable so Object; socket oldlabel Current label for so newlabel Label update for so Update the label on a socket from the passed socket label update. <function>&mac.mpo;_set_socket_peer_from_mbuf</function> void &mac.mpo;_set_socket_peer_from_mbuf struct mbuf *mbuf struct label *mbuflabel struct label *oldlabel struct label *newlabel &mac.thead; mbuf First datagram received over socket mbuflabel Label for mbuf oldlabel Current label for the socket newlabel Policy label to be filled out for the socket Set the peer label on a stream socket from the passed mbuf label. This call will be made when the first datagram is received by the stream socket, with the exception of Unix domain sockets. <function>&mac.mpo;_set_socket_peer_from_socket</function> void &mac.mpo;_set_socket_peer_from_socket struct socket *oldsocket struct label *oldsocketlabel struct socket *newsocket struct label *newsocketpeerlabel &mac.thead; oldsocket Local socket oldsocketlabel Policy label for oldsocket newsocket Peer socket newsocketpeerlabel Policy label to fill in for newsocket Set the peer label on a stream UNIX domain socket from the passed remote socket endpoint. This call will be made when the socket pair is connected, and will be made for both endpoints. Network Object Labeling Event Operations <function>&mac.mpo;_create_bpfdesc</function> void &mac.mpo;_create_bpfdesc struct ucred *cred struct bpf_d *bpf_d struct label *bpflabel &mac.thead; cred Subject credential Immutable bpf_d Object; bpf descriptor bpf Policy label to be filled in for bpf_d Set the label on a newly created BPF descriptor from the passed subject credential. This call will be made when a BPF device node is opened by a process with the passed subject credential. <function>&mac.mpo;_create_ifnet</function> void &mac.mpo;_create_ifnet struct ifnet *ifnet struct label *ifnetlabel &mac.thead; ifnet Network interface ifnetlabel Policy label to fill in for ifnet Set the label on a newly created interface. This call may be made when a new physical interface becomes available to the system, or when a pseudo-interface is instantiated during the boot or as a result of a user action. <function>&mac.mpo;_create_ipq</function> void &mac.mpo;_create_ipq struct mbuf *fragment struct label *fragmentlabel struct ipq *ipq struct label *ipqlabel &mac.thead; fragment First received IP fragment fragmentlabel Policy label for fragment ipq IP reassembly queue to be labeled ipqlabel Policy label to be filled in for ipq Set the label on a newly created IP fragment reassembly queue from the mbuf header of the first received fragment. <function>&mac.mpo;_create_datagram_from_ipq</function> void &mac.mpo;_create_create_datagram_from_ipq struct ipq *ipq struct label *ipqlabel struct mbuf *datagram struct label *datagramlabel &mac.thead; ipq IP reassembly queue ipqlabel Policy label for ipq datagram Datagram to be labeled datagramlabel Policy label to be filled in for datagramlabel Set the label on a newly reassembled IP datagram from the IP fragment reassembly queue from which it was generated. <function>&mac.mpo;_create_fragment</function> void &mac.mpo;_create_fragment struct mbuf *datagram struct label *datagramlabel struct mbuf *fragment struct label *fragmentlabel &mac.thead; datagram Datagram datagramlabel Policy label for datagram fragment Fragment to be labeled fragmentlabel Policy label to be filled in for datagram Set the label on the mbuf header of a newly created IP fragment from the label on the mbuf header of the datagram it was generate from. <function>&mac.mpo;_create_mbuf_from_mbuf</function> void &mac.mpo;_create_mbuf_from_mbuf struct mbuf *oldmbuf struct label *oldmbuflabel struct mbuf *newmbuf struct label *newmbuflabel &mac.thead; oldmbuf Existing (source) mbuf oldmbuflabel Policy label for oldmbuf newmbuf New mbuf to be labeled newmbuflabel Policy label to be filled in for newmbuf Set the label on the mbuf header of a newly created datagram from the mbuf header of an existing datagram. This call may be made in a number of situations, including when an mbuf is re-allocated for alignment purposes. <function>&mac.mpo;_create_mbuf_linklayer</function> void &mac.mpo;_create_mbuf_linklayer struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; ifnet Network interface ifnetlabel Policy label for ifnet mbuf mbuf header for new datagram mbuflabel Policy label to be filled in for mbuf Set the label on the mbuf header of a newly created datagram generated for the purposes of a link layer response for the passed interface. This call may be made in a number of situations, including for ARP or ND6 responses in the IPv4 and IPv6 stacks. <function>&mac.mpo;_create_mbuf_from_bpfdesc</function> void &mac.mpo;_create_mbuf_from_bpfdesc struct bpf_d *bpf_d struct label *bpflabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; bpf_d BPF descriptor bpflabel Policy label for bpflabel mbuf New mbuf to be labeled mbuflabel Policy label to fill in for mbuf Set the label on the mbuf header of a newly created datagram generated using the passed BPF descriptor. This call is made when a write is performed to the BPF device associated with the passed BPF descriptor. <function>&mac.mpo;_create_mbuf_from_ifnet</function> void &mac.mpo;_create_mbuf_from_ifnet struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; ifnet Network interface ifnetlabel Policy label for ifnetlabel mbuf mbuf header for new datagram mbuflabel Policy label to be filled in for mbuf Set the label on the mbuf header of a newly created datagram generated from the passed network interface. <function>&mac.mpo;_create_mbuf_multicast_encap</function> void &mac.mpo;_create_mbuf_multicast_encap struct mbuf *oldmbuf struct label *oldmbuflabel struct ifnet *ifnet struct label *ifnetlabel struct mbuf *newmbuf struct label *newmbuflabel &mac.thead; oldmbuf mbuf header for existing datagram oldmbuflabel Policy label for oldmbuf ifnet Network interface ifnetlabel Policy label for ifnet newmbuf mbuf header to be labeled for new datagram newmbuflabel Policy label to be filled in for newmbuf Set the label on the mbuf header of a newly created datagram generated from the existing passed datagram when it is processed by the passed multicast encapsulation interface. This call is made when an mbuf is to be delivered using the virtual interface. <function>&mac.mpo;_create_mbuf_netlayer</function> void &mac.mpo;_create_mbuf_netlayer struct mbuf *oldmbuf struct label *oldmbuflabel struct mbuf *newmbuf struct label *newmbuflabel &mac.thead; oldmbuf Received datagram oldmbuflabel Policy label for oldmbuf newmbuf Newly created datagram newmbuflabel Policy label for newmbuf Set the label on the mbuf header of a newly created datagram generated by the IP stack in response to an existing received datagram (oldmbuf). This call may be made in a number of situations, including when responding to ICMP request datagrams. <function>&mac.mpo;_fragment_match</function> int &mac.mpo;_fragment_match struct mbuf *fragment struct label *fragmentlabel struct ipq *ipq struct label *ipqlabel &mac.thead; fragment IP datagram fragment fragmentlabel Policy label for fragment ipq IP fragment reassembly queue ipqlabel Policy label for ipq Determine whether an mbuf header containing an IP datagram (fragment) fragment matches the label of the passed IP fragment reassembly queue (ipq). Return (1) for a successful match, or (0) for no match. This call is made when the IP stack attempts to find an existing fragment reassembly queue for a newly received fragment; if this fails, a new fragment reassembly queue may be instantiated for the fragment. Policies may use this entry point to prevent the reassembly of otherwise matching IP fragments if policy does not permit them to be reassembled based on the label or other information. <function>&mac.mpo;_relabel_ifnet</function> void &mac.mpo;_relabel_ifnet struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct label *newlabel &mac.thead; cred Subject credential ifnet Object; Network interface ifnetlabel Policy label for ifnet newlabel Label update to apply to ifnet Update the label of network interface, ifnet, based on the passed update label, newlabel, and the passed subject credential, cred. <function>&mac.mpo;_update_ipq</function> void &mac.mpo;_update_ipq struct mbuf *fragment struct label *fragmentlabel struct ipq *ipq struct label *ipqlabel &mac.thead; mbuf IP fragment mbuflabel Policy label for mbuf ipq IP fragment reassembly queue ipqlabel Policy label to be updated for ipq Update the label on an IP fragment reassembly queue (ipq) based on the acceptance of the passed IP fragment mbuf header (mbuf). Process Labeling Event Operations <function>&mac.mpo;_create_cred</function> void &mac.mpo;_create_cred struct ucred *parent_cred struct ucred *child_cred &mac.thead; parent_cred Parent subject credential child_cred Child subject credential Set the label of a newly created subject credential from the passed subject credential. This call will be made when &man.crcopy.9; is invoked on a newly created struct ucred. This call should not be confused with a process forking or creation event. <function>&mac.mpo;_execve_transition</function> void &mac.mpo;_execve_transition struct ucred *old struct ucred *new struct vnode *vp struct label *vnodelabel &mac.thead; old Existing subject credential Immutable new New subject credential to be labeled vp File to execute Locked vnodelabel Policy label for vp Update the label of a newly created subject credential (new) from the passed existing subject credential (old) based on a label transition caused by executing the passed vnode (vp). This call occurs when a process executes the passed vnode and one of the policies returns a success from the mpo_execve_will_transition entry point. Policies may choose to implement this call simply by invoking mpo_create_cred and passing the two subject credentials so as not to implement a transitioning event. Policies should not leave this entry point unimplemented if they implement mpo_create_cred, even if they do not implement mpo_execve_will_transition. <function>&mac.mpo;_execve_will_transition</function> int &mac.mpo;_execve_will_transition struct ucred *old struct vnode *vp struct label *vnodelabel &mac.thead; old Subject credential prior to &man.execve.2; Immutable vp File to execute vnodelabel Policy label for vp Determine whether the policy will want to perform a transition event as a result of the execution of the passed vnode by the passed subject credential. Return 1 if a transition is required, 0 if not. Even if a policy returns 0, it should behave correctly in the presence of an unexpected invocation of mpo_execve_transition, as that call may happen as a result of another policy requesting a transition. <function>&mac.mpo;_create_proc0</function> void &mac.mpo;_create_proc0 struct ucred *cred &mac.thead; cred Subject credential to be filled in Create the subject credential of process 0, the parent of all kernel processes. <function>&mac.mpo;_create_proc1</function> void &mac.mpo;_create_proc1 struct ucred *cred &mac.thead; cred Subject credential to be filled in Create the subject credential of process 1, the parent of all user processes. <function>&mac.mpo;_relabel_cred</function> void &mac.mpo;_relabel_cred struct ucred *cred struct label *newlabel &mac.thead; cred Subject credential newlabel Label update to apply to cred Update the label on a subject credential from the passed update label. Access Control Checks Access control entry points permit policy modules to influence access control decisions made by the kernel. Generally, although not always, arguments to an access control entry point will include one or more authorizing credentials, information (possibly including a label) for any other objects involved in the operation. An access control entry point may return 0 to permit the operation, or an &man.errno.2; error value. The results of invoking the entry point across various registered policy modules will be composed as follows: if all modules permit the operation to succeed, success will be returned. If one or modules returns a failure, a failure will be returned. If more than one module returns a failure, the errno value to return to the user will be selected using the following precedence, implemented by the error_select() function in kern_mac.c: Most precedence EDEADLK EINVAL ESRCH EACCES Least precedence EPERM If none of the error values returned by all modules are listed in the precedence chart then an arbitrarily selected value from the set will be returned. In general, the rules provide precedence to errors in the following order: kernel failures, invalid arguments, object not present, access not permitted, other. <function>&mac.mpo;_check_bpfdesc_receive</function> int &mac.mpo;_check_bpfdesc_receive struct bpf_d *bpf_d struct label *bpflabel struct ifnet *ifnet struct label *ifnetlabel &mac.thead; bpf_d Subject; BPF descriptor bpflabel Policy label for bpf_d ifnet Object; network interface ifnetlabel Policy label for ifnet Determine whether the MAC framework should permit datagrams from the passed interface to be delivered to the buffers of the passed BPF descriptor. Return (0) for success, or an errno value for failure Suggested failure: EACCES for label mismatches, EPERM for lack of privilege. <function>&mac.mpo;_check_kenv_dump</function> int &mac.mpo;_check_kenv_dump struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to retrieve the kernel environment (see &man.kenv.2;). <function>&mac.mpo;_check_kenv_get</function> int &mac.mpo;_check_kenv_get struct ucred *cred char *name &mac.thead; cred Subject credential name Kernel environment variable name Determine whether the subject should be allowed to retrieve the value of the specified kernel environment variable. <function>&mac.mpo;_check_kenv_set</function> int &mac.mpo;_check_kenv_set struct ucred *cred char *name &mac.thead; cred Subject credential name Kernel environment variable name Determine whether the subject should be allowed to set the specified kernel environment variable. <function>&mac.mpo;_check_kenv_unset</function> int &mac.mpo;_check_kenv_unset struct ucred *cred char *name &mac.thead; cred Subject credential name Kernel environment variable name Determine whether the subject should be allowed to unset the specified kernel environment variable. <function>&mac.mpo;_check_kld_load</function> int &mac.mpo;_check_kld_load struct ucred *cred struct vnode *vp struct label *vlabel &mac.thead; cred Subject credential vp Kernel module vnode vlabel Label associated with vp Determine whether the subject should be allowed to load the specified module file. <function>&mac.mpo;_check_kld_stat</function> int &mac.mpo;_check_kld_stat struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to retrieve a list of loaded kernel module files and associated statistics. <function>&mac.mpo;_check_kld_unload</function> int &mac.mpo;_check_kld_unload struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to unload a kernel module. <function>&mac.mpo;_check_pipe_ioctl</function> int &mac.mpo;_check_pipe_ioctl struct ucred *cred struct pipe *pipe struct label *pipelabel unsigned long cmd void *data &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe cmd &man.ioctl.2; command data &man.ioctl.2; data Determine whether the subject should be allowed to make the specified &man.ioctl.2; call. <function>&mac.mpo;_check_pipe_poll</function> int &mac.mpo;_check_pipe_poll struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed to poll pipe. <function>&mac.mpo;_check_pipe_read</function> int &mac.mpo;_check_pipe_read struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed read access to pipe. <function>&mac.mpo;_check_pipe_relabel</function> int &mac.mpo;_check_pipe_relabel struct ucred *cred struct pipe *pipe struct label *pipelabel struct label *newlabel &mac.thead; cred Subject credential pipe Pipe pipelabel Current policy label associated with pipe newlabel Label update to pipelabel Determine whether the subject should be allowed to relabel pipe. <function>&mac.mpo;_check_pipe_stat</function> int &mac.mpo;_check_pipe_stat struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed to retrieve statistics related to pipe. <function>&mac.mpo;_check_pipe_write</function> int &mac.mpo;_check_pipe_write struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed to write to pipe. <function>&mac.mpo;_check_socket_bind</function> int &mac.mpo;_check_socket_bind struct ucred *cred struct socket *socket struct label *socketlabel struct sockaddr *sockaddr &mac.thead; cred Subject credential socket Socket to be bound socketlabel Policy label for socket sockaddr Address of socket <function>&mac.mpo;_check_socket_connect</function> int &mac.mpo;_check_socket_connect struct ucred *cred struct socket *socket struct label *socketlabel struct sockaddr *sockaddr &mac.thead; cred Subject credential socket Socket to be connected socketlabel Policy label for socket sockaddr Address of socket Determine whether the subject credential (cred) can connect the passed socket (socket) to the passed socket address (sockaddr). Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches, EPERM for lack of privilege. <function>&mac.mpo;_check_socket_receive</function> int &mac.mpo;_check_socket_receive struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential so Socket socketlabel Policy label associated with so Determine whether the subject should be allowed to receive information from the socket so. <function>&mac.mpo;_check_socket_send</function> int &mac.mpo;_check_socket_send struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential so Socket socketlabel Policy label associated with so Determine whether the subject should be allowed to send information across the socket so. <function>&mac.mpo;_check_cred_visible</function> int &mac.mpo;_check_cred_visible struct ucred *u1 struct ucred *u2 &mac.thead; u1 Subject credential u2 Object credential Determine whether the subject credential u1 can see other subjects with the passed subject credential u2. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches, EPERM for lack of privilege, or ESRCH to hide visibility. This call may be made in a number of situations, including inter-process status sysctls used by ps, and in procfs lookups. <function>&mac.mpo;_check_socket_visible</function> int &mac.mpo;_check_socket_visible struct ucred *cred struct socket *socket struct label *socketlabel &mac.thead; cred Subject credential socket Object; socket socketlabel Policy label for socket <function>&mac.mpo;_check_ifnet_relabel</function> int &mac.mpo;_check_ifnet_relabel struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct label *newlabel &mac.thead; cred Subject credential ifnet Object; network interface ifnetlabel Existing policy label for ifnet newlabel Policy label update to later be applied to ifnet Determine whether the subject credential can relabel the passed network interface to the passed label update. <function>&mac.mpo;_check_socket_relabel</function> int &mac.mpo;_check_socket_relabel struct ucred *cred struct socket *socket struct label *socketlabel struct label *newlabel &mac.thead; cred Subject credential socket Object; socket socketlabel Existing policy label for socket newlabel Label update to later be applied to socketlabel Determine whether the subject credential can relabel the passed socket to the passed label update. <function>&mac.mpo;_check_cred_relabel</function> int &mac.mpo;_check_cred_relabel struct ucred *cred struct label *newlabel &mac.thead; cred Subject credential newlabel Label update to later be applied to cred Determine whether the subject credential can relabel itself to the passed label update. <function>&mac.mpo;_check_vnode_relabel</function> int &mac.mpo;_check_vnode_relabel struct ucred *cred struct vnode *vp struct label *vnodelabel struct label *newlabel &mac.thead; cred Subject credential Immutable vp Object; vnode Locked vnodelabel Existing policy label for vp newlabel Policy label update to later be applied to vp Determine whether the subject credential can relabel the passed vnode to the passed label update. <function>&mac.mpo;_check_mount_stat</function> int &mac.mpo;_check_mount_stat struct ucred *cred struct mount *mp struct label *mountlabel &mac.thead; cred Subject credential mp Object; file system mount mountlabel Policy label for mp Determine whether the subject credential can see the results of a statfs performed on the file system. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches or EPERM for lack of privilege. This call may be made in a number of situations, including during invocations of &man.statfs.2; and related calls, as well as to determine what file systems to exclude from listings of file systems, such as when &man.getfsstat.2; is invoked. <function>&mac.mpo;_check_proc_debug</function> int &mac.mpo;_check_proc_debug struct ucred *cred struct proc *proc &mac.thead; cred Subject credential Immutable proc Object; process Determine whether the subject credential can debug the passed process. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, EPERM for lack of privilege, or ESRCH to hide visibility of the target. This call may be made in a number of situations, including use of the &man.ptrace.2; and &man.ktrace.2; APIs, as well as for some types of procfs operations. <function>&mac.mpo;_check_vnode_access</function> int &mac.mpo;_check_vnode_access struct ucred *cred struct vnode *vp struct label *label int flags &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp flags &man.access.2; flags Determine how invocations of &man.access.2; and related calls by the subject credential should return when performed on the passed vnode using the passed access flags. This should generally be implemented using the same semantics used in &mac.mpo;_check_vnode_open. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_chdir</function> int &mac.mpo;_check_vnode_chdir struct ucred *cred struct vnode *dvp struct label *dlabel &mac.thead; cred Subject credential dvp Object; vnode to &man.chdir.2; into dlabel Policy label for dvp Determine whether the subject credential can change the process working directory to the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_chroot</function> int &mac.mpo;_check_vnode_chroot struct ucred *cred struct vnode *dvp struct label *dlabel &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp Determine whether the subject should be allowed to &man.chroot.2; into the specified directory (dvp). <function>&mac.mpo;_check_vnode_create</function> int &mac.mpo;_check_vnode_create struct ucred *cred struct vnode *dvp struct label *dlabel struct componentname *cnp struct vattr *vap &mac.thead; cred Subject credential dvp Object; vnode dlabel Policy label for dvp cnp Component name for dvp vap vnode attributes for vap Determine whether the subject credential can create a vnode with the passed parent directory, passed name information, and passed attribute information. Return 0 for success, or an errno value for failure. Suggested failure: EACCES. for label mismatch, or EPERM for lack of privilege. This call may be made in a number of situations, including as a result of calls to &man.open.2; with O_CREAT, &man.mknod.2;, &man.mkfifo.2;, and others. <function>&mac.mpo;_check_vnode_delete</function> int &mac.mpo;_check_vnode_delete struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp void *label struct componentname *cnp &mac.thead; cred Subject credential dvp Parent directory vnode dlabel Policy label for dvp vp Object; vnode to delete label Policy label for vp cnp Component name for vp Determine whether the subject credential can delete a vnode from the passed parent directory and passed name information. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. This call may be made in a number of situations, including as a result of calls to &man.unlink.2; and &man.rmdir.2;. Policies implementing this entry point should also implement mpo_check_rename_to to authorize deletion of objects as a result of being the target of a rename. <function>&mac.mpo;_check_vnode_deleteacl</function> int &mac.mpo;_check_vnode_deleteacl struct ucred *cred struct vnode *vp struct label *label acl_type_t type &mac.thead; cred Subject credential Immutable vp Object; vnode Locked label Policy label for vp type ACL type Determine whether the subject credential can delete the ACL of passed type from the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_exec</function> int &mac.mpo;_check_vnode_exec struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode to execute label Policy label for vp Determine whether the subject credential can execute the passed vnode. Determination of execute privilege is made separately from decisions about any transitioning event. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_getacl</function> int &mac.mpo;_check_vnode_getacl struct ucred *cred struct vnode *vp struct label *label acl_type_t type &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp type ACL type Determine whether the subject credential can retrieve the ACL of passed type from the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_getextattr</function> int &mac.mpo;_check_vnode_getextattr struct ucred *cred struct vnode *vp struct label *label int attrnamespace const char *name struct uio *uio &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp attrnamespace Extended attribute namespace name Extended attribute name uio I/O structure pointer; see &man.uio.9; Determine whether the subject credential can retrieve the extended attribute with the passed namespace and name from the passed vnode. Policies implementing labeling using extended attributes may be interested in special handling of operations on those extended attributes. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_link</function> int &mac.mpo;_check_vnode_link struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp struct label *label struct componentname *cnp &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp vp Link destination vnode label Policy label associated with vp cnp Component name for the link being created Determine whether the subject should be allowed to create a link to the vnode vp with the name specified by cnp. <function>&mac.mpo;_check_vnode_mmap</function> int &mac.mpo;_check_vnode_mmap struct ucred *cred struct vnode *vp struct label *label int prot &mac.thead; cred Subject credential vp Vnode to map label Policy label associated with vp prot Mmap protections (see &man.mmap.2;) Determine whether the subject should be allowed to map the vnode vp with the protections specified in prot. <function>&mac.mpo;_check_vnode_mmap_downgrade</function> void &mac.mpo;_check_vnode_mmap_downgrade struct ucred *cred struct vnode *vp struct label *label int *prot &mac.thead; cred See . vp label prot Mmap protections to be downgraded Downgrade the mmap protections based on the subject and object labels. <function>&mac.mpo;_check_vnode_mprotect</function> int &mac.mpo;_check_vnode_mprotect struct ucred *cred struct vnode *vp struct label *label int prot &mac.thead; cred Subject credential vp Mapped vnode prot Memory protections Determine whether the subject should be allowed to set the specified memory protections on memory mapped from the vnode vp. <function>&mac.mpo;_check_vnode_poll</function> int &mac.mpo;_check_vnode_poll struct ucred *active_cred struct ucred *file_cred struct vnode *vp struct label *label &mac.thead; active_cred Subject credential file_cred Credential associated with the struct file vp Polled vnode label Policy label associated with vp Determine whether the subject should be allowed to poll the vnode vp. <function>&mac.mpo;_check_vnode_rename_from</function> int &mac.mpo;_vnode_rename_from struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp struct label *label struct componentname *cnp &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp vp Vnode to be renamed label Policy label associated with vp cnp Component name for vp Determine whether the subject should be allowed to rename the vnode vp to something else. <function>&mac.mpo;_check_vnode_rename_to</function> int &mac.mpo;_check_vnode_rename_to struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp struct label *label int samedir struct componentname *cnp &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp vp Overwritten vnode label Policy label associated with vp samedir Boolean; 1 if the source and destination directories are the same cnp Destination component name Determine whether the subject should be allowed to rename to the vnode vp, into the directory dvp, or to the name represented by cnp. If there is no existing file to overwrite, vp and label will be NULL. <function>&mac.mpo;_check_socket_listen</function> int &mac.mpo;_check_socket_listen struct ucred *cred struct socket *socket struct label *socketlabel &mac.thead; cred Subject credential socket Object; socket socketlabel Policy label for socket Determine whether the subject credential can listen on the passed socket. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_lookup</function> int &mac.mpo;_check_vnode_lookup struct ucred *cred struct vnode *dvp struct label *dlabel struct componentname *cnp &mac.thead; cred Subject credential dvp Object; vnode dlabel Policy label for dvp cnp Component name being looked up Determine whether the subject credential can perform a lookup in the passed directory vnode for the passed name. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_open</function> int &mac.mpo;_check_vnode_open struct ucred *cred struct vnode *vp struct label *label int acc_mode &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp acc_mode &man.open.2; access mode Determine whether the subject credential can perform an open operation on the passed vnode with the passed access mode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_readdir</function> int &mac.mpo;_check_vnode_readdir struct ucred *cred struct vnode *dvp struct label *dlabel &mac.thead; cred Subject credential dvp Object; directory vnode dlabel Policy label for dvp Determine whether the subject credential can perform a readdir operation on the passed directory vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_readlink</function> int &mac.mpo;_check_vnode_readlink struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp Determine whether the subject credential can perform a readlink operation on the passed symlink vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. This call may be made in a number of situations, including an explicit readlink call by the user process, or as a result of an implicit readlink during a name lookup by the process. <function>&mac.mpo;_check_vnode_revoke</function> int &mac.mpo;_check_vnode_revoke struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp Determine whether the subject credential can revoke access to the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setacl</function> int &mac.mpo;_check_vnode_setacl struct ucred *cred struct vnode *vp struct label *label acl_type_t type struct acl *acl &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp type ACL type acl ACL Determine whether the subject credential can set the passed ACL of passed type on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setextattr</function> int &mac.mpo;_check_vnode_setextattr struct ucred *cred struct vnode *vp struct label *label int attrnamespace const char *name struct uio *uio &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp attrnamespace Extended attribute namespace name Extended attribute name uio I/O structure pointer; see &man.uio.9; Determine whether the subject credential can set the extended attribute of passed name and passed namespace on the passed vnode. Policies implementing security labels backed into extended attributes may want to provide additional protections for those attributes. Additionally, policies should avoid making decisions based on the data referenced from uio, as there is a potential race condition between this check and the actual operation. The uio may also be NULL if a delete operation is being performed. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setflags</function> int &mac.mpo;_check_vnode_setflags struct ucred *cred struct vnode *vp struct label *label u_long flags &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp flags File flags; see &man.chflags.2; Determine whether the subject credential can set the passed flags on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setmode</function> int &mac.mpo;_check_vnode_setmode struct ucred *cred struct vnode *vp struct label *label mode_t mode &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp mode File mode; see &man.chmod.2; Determine whether the subject credential can set the passed mode on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setowner</function> int &mac.mpo;_check_vnode_setowner struct ucred *cred struct vnode *vp struct label *label uid_t uid gid_t gid &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp uid User ID gid Group ID Determine whether the subject credential can set the passed uid and passed gid as file uid and file gid on the passed vnode. The IDs may be set to (-1) to request no update. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setutimes</function> int &mac.mpo;_check_vnode_setutimes struct ucred *cred struct vnode *vp struct label *label struct timespec atime struct timespec mtime &mac.thead; cred Subject credential vp Object; vp label Policy label for vp atime Access time; see &man.utimes.2; mtime Modification time; see &man.utimes.2; Determine whether the subject credential can set the passed access timestamps on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_proc_sched</function> int &mac.mpo;_check_proc_sched struct ucred *ucred struct proc *proc &mac.thead; cred Subject credential proc Object; process Determine whether the subject credential can change the scheduling parameters of the passed process. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, EPERM for lack of privilege, or ESRCH to limit visibility. See &man.setpriority.2; for more information. <function>&mac.mpo;_check_proc_signal</function> int &mac.mpo;_check_proc_signal struct ucred *cred struct proc *proc int signal &mac.thead; cred Subject credential proc Object; process signal Signal; see &man.kill.2; Determine whether the subject credential can deliver the passed signal to the passed process. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, EPERM for lack of privilege, or ESRCH to limit visibility. <function>&mac.mpo;_check_vnode_stat</function> int &mac.mpo;_check_vnode_stat struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp Determine whether the subject credential can stat the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. See &man.stat.2; for more information. <function>&mac.mpo;_check_ifnet_transmit</function> int &mac.mpo;_check_ifnet_transmit struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; cred Subject credential ifnet Network interface ifnetlabel Policy label for ifnet mbuf Object; mbuf to be sent mbuflabel Policy label for mbuf Determine whether the network interface can transmit the passed mbuf. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_socket_deliver</function> int &mac.mpo;_check_socket_deliver struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; cred Subject credential ifnet Network interface ifnetlabel Policy label for ifnet mbuf Object; mbuf to be delivered mbuflabel Policy label for mbuf Determine whether the socket may receive the datagram stored in the passed mbuf header. Return 0 for success, or an errno value for failure. Suggested failures: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_socket_visible</function> int &mac.mpo;_check_socket_visible struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential Immutable so Object; socket socketlabel Policy label for so Determine whether the subject credential cred can "see" the passed socket (socket) using system monitoring functions, such as those employed by &man.netstat.8; and &man.sockstat.1;. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches, EPERM for lack of privilege, or ESRCH to hide visibility. <function>&mac.mpo;_check_system_acct</function> int &mac.mpo;_check_system_acct struct ucred *ucred struct vnode *vp struct label *vlabel &mac.thead; ucred Subject credential vp Accounting file; &man.acct.5; vlabel Label associated with vp Determine whether the subject should be allowed to enable accounting, based on its label and the label of the accounting log file. <function>&mac.mpo;_check_system_nfsd</function> int &mac.mpo;_check_system_nfsd struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to call &man.nfssvc.2;. <function>&mac.mpo;_check_system_reboot</function> int &mac.mpo;_check_system_reboot struct ucred *cred int howto &mac.thead; cred Subject credential howto howto parameter from &man.reboot.2; Determine whether the subject should be allowed to reboot the system in the specified manner. <function>&mac.mpo;_check_system_settime</function> int &mac.mpo;_check_system_settime struct ucred *cred &mac.thead; cred Subject credential Determine whether the user should be allowed to set the system clock. <function>&mac.mpo;_check_system_swapon</function> int &mac.mpo;_check_system_swapon struct ucred *cred struct vnode *vp struct label *vlabel &mac.thead; cred Subject credential vp Swap device vlabel Label associated with vp Determine whether the subject should be allowed to add vp as a swap device. <function>&mac.mpo;_check_system_sysctl</function> int &mac.mpo;_check_system_sysctl struct ucred *cred int *name u_int *namelen void *old size_t *oldlenp int inkernel void *new size_t newlen &mac.thead; cred Subject credential name See &man.sysctl.3; namelen old oldlenp inkernel Boolean; 1 if called from kernel new See &man.sysctl.3; newlen Determine whether the subject should be allowed to make the specified &man.sysctl.3; transaction. Label Management Calls Relabel events occur when a user process has requested that the label on an object be modified. A two-phase update occurs: first, an access control check will be performed to determine if the update is both valid and permitted, and then the update itself is performed via a separate entry point. Relabel entry points typically accept the object, object label reference, and an update label submitted by the process. Memory allocation during relabel is discouraged, as relabel calls are not permitted to fail (failure should be reported earlier in the relabel check). Userland Architecture The TrustedBSD MAC Framework includes a number of policy-agnostic elements, including MAC library interfaces for abstractly managing labels, modifications to the system credential management and login libraries to support the assignment of MAC labels to users, and a set of tools to monitor and modify labels on processes, files, and network interfaces. More details on the user architecture will be added to this section in the near future. APIs for Policy-Agnostic Label Management The TrustedBSD MAC Framework provides a number of library and system calls permitting applications to manage MAC labels on objects using a policy-agnostic interface. This permits applications to manipulate labels for a variety of policies without being written to support specific policies. These interfaces are used by general-purpose tools such as &man.ifconfig.8;, &man.ls.1; and &man.ps.1; to view labels on network interfaces, files, and processes. The APIs also support MAC management tools including &man.getfmac.8;, &man.getpmac.8;, &man.setfmac.8;, &man.setfsmac.8;, and &man.setpmac.8;. The MAC APIs are documented in &man.mac.3;. Applications handle MAC labels in two forms: an internalized form used to return and set labels on processes and objects (mac_t), and externalized form based on C strings appropriate for storage in configuration files, display to the user, or input from the user. Each MAC label contains a number of elements, each consisting of a name and value pair. Policy modules in the kernel bind to specific names and interpret the values in policy-specific ways. In the externalized string form, labels are represented by a comma-delimited list of name and value pairs separated by the / character. Labels may be directly converted to and from text using provided APIs; when retrieving labels from the kernel, internalized label storage must first be prepared for the desired label element set. Typically, this is done in one of two ways: using &man.mac.prepare.3; and an arbitrary list of desired label elements, or one of the variants of the call that loads a default element set from the &man.mac.conf.5; configuration file. Per-object defaults permit application writers to usefully display labels associated with objects without being aware of the policies present in the system. Currently, direct manipulation of label elements other than by conversion to a text string, string editing, and conversion back to an internalized label is not supported by the MAC library. Such interfaces may be added in the future if they prove necessary for application writers. Binding of Labels to Users The standard user context management interface, &man.setusercontext.3;, has been modified to retrieve MAC labels associated with a user's class from &man.login.conf.5;. These labels are then set along with other user context when either LOGIN_SETALL is specified, or when LOGIN_SETMAC is explicitly specified. It is expected that, in a future version of FreeBSD, the MAC label database will be separated from the login.conf user class abstraction, and be maintained in a separate database. However, the &man.setusercontext.3; API should remain the same following such a change. Conclusion The TrustedBSD MAC framework permits kernel modules to augment the system security policy in a highly integrated manner. They may do this based on existing object properties, or based on label data that is maintained with the assistance of the MAC framework. The framework is sufficiently flexible to implement a variety of policy types, including information flow security policies such as MLS and Biba, as well as policies based on existing BSD credentials or file protections. Policy authors may wish to consult this documentation as well as existing security modules when implementing a new security service.
diff --git a/en_US.ISO8859-1/books/arch-handbook/newbus/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/newbus/chapter.sgml index 4fda867ee6..c20617c4fc 100644 --- a/en_US.ISO8859-1/books/arch-handbook/newbus/chapter.sgml +++ b/en_US.ISO8859-1/books/arch-handbook/newbus/chapter.sgml @@ -1,360 +1,360 @@ Jeroen Ruigrok van der Werven (asmodai)
asmodai@FreeBSD.org
Written by
Hiten Pandya
hiten@uk.FreeBSD.org
Newbus Special thanks to Matthew N. Dodd, Warner Losh, Bill Paul, Doug Rabson, Mike Smith, Peter Wemm and Scott Long. This chapter explains the Newbus device framework in detail. Device Drivers Purpose of a Device Driver A device driver is a software component which provides the interface between the kernel's generic view of a peripheral (e.g. disk, network adapter) and the actual implementation of the peripheral. The device driver interface (DDI) is the defined interface between the kernel and the device driver component. Types of Device Drivers There used to be days in &unix;, and thus FreeBSD, in which there were four types of devices defined: block device drivers character device drivers network device drivers pseudo-device drivers Block devices performed in way that used fixed size blocks [of data]. This type of driver depended on the so called buffer cache, which had the purpose to cache accessed blocks of data in a dedicated part of the memory. Often this buffer cache was based on write-behind, which meant that when data was modified in memory it got synced to disk whenever the system did its periodical disk flushing, thus optimizing writes. Character devices However, in the versions of FreeBSD 4.0 and onward the distinction between block and character devices became non-existent. Overview of Newbus Newbus is the implementation of a new bus architecture based on abstraction layers which saw its introduction in FreeBSD 3.0 when the Alpha port was imported into the source tree. It was not until 4.0 before it became the default system to use for device drivers. Its goals are to provide a more object oriented means of interconnecting the various busses and devices which a host system provides to the Operating System. Its main features include amongst others: dynamic attaching easy modularization of drivers pseudo-busses One of the most prominent changes is the migration from the flat and ad-hoc system to a device tree lay-out. At the top level resides the root device which is the parent to hang all other devices on. For each architecture, there is typically a single child of root which has such things as host-to-PCI bridges, etc. attached to it. For x86, this root device is the nexus device and for Alpha, various different different models of Alpha have different top-level devices corresponding to the different hardware chipsets, including lca, apecs, cia and tsunami. A device in the Newbus context represents a single hardware entity in the system. For instance each PCI device is represented by a Newbus device. Any device in the system can have children; a device which has children is often called a bus. Examples of common busses in the system are ISA and PCI which manage lists of devices attached to ISA and PCI busses respectively. Often, a connection between different kinds of bus is represented by a bridge device which normally has one child for the attached bus. An example of this is a PCI-to-PCI bridge which is represented by a device pcibN on the parent PCI bus and has a child pciN for the attached bus. This layout simplifies the implementation of the PCI bus tree, allowing common code to be used for both top-level and bridged busses. Each device in the Newbus architecture asks its parent to map its resources. The parent then asks its own parent until the nexus is reached. So, basically the nexus is the only part of the Newbus system which knows about all resources. An ISA device might want to map its IO port at 0x230, so it asks its parent, in this case the ISA bus. The ISA bus hands it over to the PCI-to-ISA bridge which in its turn asks the PCI bus, which reaches the host-to-PCI bridge and finally the nexus. The beauty of this transition upwards is that there is room to translate the requests. For example, the 0x230 IO port request might become memory-mapped at 0xb0000230 on a MIPS box by the PCI bridge. Resource allocation can be controlled at any place in the device tree. For instance on many Alpha platforms, ISA interrupts are managed separately from PCI interrupts and resource allocations for ISA interrupts are managed by the Alpha's ISA bus device. On IA-32, ISA and PCI interrupts are both managed by the top-level nexus device. For both ports, memory and port address space is managed by a single entity - nexus for IA-32 and the relevant chipset driver on Alpha (e.g. CIA or tsunami). In order to normalize access to memory and port mapped resources, Newbus integrates the bus_space APIs from NetBSD. These provide a single API to replace inb/outb and direct memory reads/writes. The advantage of this is that a single driver can easily use either memory-mapped registers or port-mapped registers (some hardware supports both). This support is integrated into the resource allocation mechanism. When a resource is allocated, a driver can retrieve the associated bus_space_tag_t and bus_space_handle_t from the resource. Newbus also allows for definitions of interface methods in files dedicated to this purpose. These are the .m files that are found under the src/sys hierarchy. The core of the Newbus system is an extensible object-based programming model. Each device in the system has a table of methods which it supports. The system and other devices uses those methods to control the device and request services. The different methods supported by a device are defined by a number of interfaces. An interface is simply a group of related methods which can be implemented by a device. In the Newbus system, the methods for a device are provided by the various device drivers in the system. When a device is attached to a driver during auto-configuration, it uses the method table declared by the driver. A device can later detach from its driver and re-attach to a new driver with a new method table. This allows dynamic replacement of drivers which can be useful for driver development. The interfaces are described by an interface definition language similar to the language used to define vnode operations for file systems. The interface would be stored in a methods file (which would normally named foo_if.m). Newbus Methods # Foo subsystem/driver (a comment...) INTERFACE foo METHOD int doit { device_t dev; }; # DEFAULT is the method that will be used, if a method was not # provided via: DEVMETHOD() METHOD void doit_to_child { device_t dev; driver_t child; } DEFAULT doit_generic_to_child; When this interface is compiled, it generates a header file foo_if.h which contains function declarations: int FOO_DOIT(device_t dev); int FOO_DOIT_TO_CHILD(device_t dev, device_t child); A source file, foo_if.c is also created to accompany the automatically generated header file; it contains implementations of those functions which look up the location of the relevant functions in the object's method table and call that function. The system defines two main interfaces. The first fundamental interface is called device and includes methods which are relevant to all devices. Methods in the device interface include probe, attach and detach to control detection of hardware and shutdown, suspend and resume for critical event notification. The second, more complex interface is bus. This interface contains methods suitable for devices which have children, including methods to access bus specific per-device information &man.bus.generic.read.ivar.9; and &man.bus.generic.write.ivar.9;, event notification (child_detached, driver_added) and resource management (alloc_resource, activate_resource, deactivate_resource, - release_resource). + release_resource). Many methods in the bus interface are performing services for some child of the bus device. These methods would normally use the first two arguments to specify the bus providing the service and the child device which is requesting the service. To simplify driver code, many of these methods have accessor functions which lookup the parent and call a method on the parent. For instance the method BUS_TEARDOWN_INTR(device_t dev, device_t child, ...) can be called using the function bus_teardown_intr(device_t child, ...). Some bus types in the system define additional interfaces to provide access to bus-specific functionality. For instance, the PCI bus driver defines the pci interface which has two methods read_config and write_config for accessing the configuration registers of a PCI device. Newbus API As the Newbus API is huge, this section makes some effort at documenting it. More information to come in the next revision of this document. Important locations in the source hierarchy src/sys/[arch]/[arch] - Kernel code for a specific machine architecture resides in this directory. for example, the i386 architecture, or the SPARC64 architecture. src/sys/dev/[bus] - device support for a specific [bus] resides in this directory. src/sys/dev/pci - PCI bus support code resides in this directory. src/sys/[isa|pci] - PCI/ISA device drivers reside in this directory. The PCI/ISA bus support code used to exist in this directory in FreeBSD version 4.0. Important structures and type definitions devclass_t - This is a type definition of a pointer to a struct devclass. device_method_t - This is same as kobj_method_t (see src/sys/kobj.h). device_t - This is a type definition of a pointer to a struct device. device_t represents a device in the system. It is a kernel object. See src/sys/sys/bus_private.h for implementation details. driver_t - This is a type definition which, references struct driver. The driver struct is a class of the device kernel object; it also holds data private to for the driver.
<emphasis>driver_t</emphasis> implementation struct driver { KOBJ_CLASS_FIELDS; void *priv; /* driver private data */ };
A device_state_t type, which is an enumeration, device_state. It contains the possible states of a Newbus device before and after the autoconfiguration process.
Device states<emphasis>device_state_t</emphasis> /* * src/sys/sys/bus.h */ typedef enum device_state { DS_NOTPRESENT, /* not probed or probe failed */ DS_ALIVE, /* probe succeeded */ DS_ATTACHED, /* attach method called */ DS_BUSY /* device is open */ } device_state_t;
diff --git a/en_US.ISO8859-1/books/arch-handbook/sound/chapter.sgml b/en_US.ISO8859-1/books/arch-handbook/sound/chapter.sgml index aeb4656017..0a107812fa 100644 --- a/en_US.ISO8859-1/books/arch-handbook/sound/chapter.sgml +++ b/en_US.ISO8859-1/books/arch-handbook/sound/chapter.sgml @@ -1,687 +1,690 @@ Jean-Francois Dockes Contributed by Sound subsystem Introduction The FreeBSD sound subsystem cleanly separates generic sound handling issues from device-specific ones. This makes it easier to add support for new hardware. The &man.pcm.4; framework is the central piece of the sound subsystem. It mainly implements the following elements: A system call interface (read, write, ioctls) to digitized sound and mixer functions. The ioctl command set is compatible with the legacy OSS or Voxware interface, allowing common multimedia applications to be ported without modification. + Common code for processing sound data (format conversions, virtual channels). A uniform software interface to hardware-specific audio interface modules. Additional support for some common hardware interfaces (ac97), or shared hardware-specific code (ex: ISA DMA routines). The support for specific sound cards is implemented by hardware-specific drivers, which provide channel and mixer interfaces to plug into the generic pcm code. In this chapter, the term pcm will refer to the central, common part of the sound driver, as opposed to the hardware-specific modules. The prospective driver writer will of course want to start from an existing module and use the code as the ultimate reference. But, while the sound code is nice and clean, it is also mostly devoid of comments. This document tries to give an overview of the framework interface and answer some questions that may arise while adapting the existing code. As an alternative, or in addition to starting from a working example, you can find a commented driver template at http://people.FreeBSD.org/~cg/template.c Files All the relevant code currently (FreeBSD 4.4) lives in /usr/src/sys/dev/sound/, except for the public ioctl interface definitions, found in /usr/src/sys/sys/soundcard.h Under /usr/src/sys/dev/sound/, the pcm/ directory holds the central code, while the isa/ and pci/ directories have the drivers for ISA and PCI boards. Probing, attaching, etc. Sound drivers probe and attach in almost the same way as any hardware driver module. You might want to look at the ISA or PCI specific sections of the handbook for more information. However, sound drivers differ in some ways: They declare themselves as pcm class devices, with a struct snddev_info device private structure: static driver_t xxx_driver = { "pcm", xxx_methods, sizeof(struct snddev_info) }; DRIVER_MODULE(snd_xxxpci, pci, xxx_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_xxxpci, snd_pcm, PCM_MINVER, PCM_PREFVER,PCM_MAXVER); Most sound drivers need to store additional private information about their device. A private data structure is usually allocated in the attach routine. Its address is passed to pcm by the calls to pcm_register() and mixer_init(). pcm later passes back this address as a parameter in calls to the sound driver interfaces. The sound driver attach routine should declare its MIXER or AC97 interface to pcm by calling mixer_init(). For a MIXER interface, this causes in turn a call to xxxmixer_init(). The sound driver attach routine declares its general CHANNEL configuration to pcm by calling pcm_register(dev, sc, nplay, nrec), where sc is the address for the device data structure, used in further calls from pcm, and nplay and nrec are the number of play and record channels. The sound driver attach routine declares each of its channel objects by calls to pcm_addchan(). This sets up the channel glue in pcm and causes in turn a call to xxxchannel_init(). The sound driver detach routine should call pcm_unregister() before releasing its resources. There are two possible methods to handle non-PnP devices: Use a device_identify() method (example: sound/isa/es1888.c). The device_identify() method probes for the hardware at known addresses and, if it finds a supported device, creates a new pcm device which is then passed to probe/attach. Use a custom kernel configuration with appropriate hints for pcm devices (example: sound/isa/mss.c). pcm drivers should implement device_suspend, device_resume and device_shutdown routines, so that power management and module unloading function correctly. Interfaces The interface between the pcm core and the sound drivers is defined in terms of kernel objects. There are two main interfaces that a sound driver will usually provide: CHANNEL and either MIXER or AC97. The AC97 interface is a very small hardware access (register read/write) interface, implemented by drivers for hardware with an AC97 codec. In this case, the actual MIXER interface is provided by the shared AC97 code in pcm. The CHANNEL interface Common notes for function parameters Sound drivers usually have a private data structure to describe their device, and one structure for each play and record data channel that it supports. For all CHANNEL interface functions, the first parameter is an opaque pointer. The second parameter is a pointer to the private channel data structure, except for channel_init() which has a pointer to the private device structure (and returns the channel pointer for further use by pcm). Overview of data transfer operations For sound data transfers, the pcm core and the sound drivers communicate through a shared memory area, described by a struct snd_dbuf. struct snd_dbuf is private to pcm, and sound drivers obtain values of interest by calls to accessor functions (sndbuf_getxxx()). The shared memory area has a size of sndbuf_getsize() and is divided into fixed size blocks of sndbuf_getblksz() bytes. When playing, the general transfer mechanism is as follows (reverse the idea for recording): pcm initially fills up the buffer, then calls the sound driver's xxxchannel_trigger() function with a parameter of PCMTRIG_START. The sound driver then arranges to repeatedly transfer the whole memory area (sndbuf_getbuf(), sndbuf_getsize()) to the device, in blocks of sndbuf_getblksz() bytes. It calls back the chn_intr() pcm function for each transferred block (this will typically happen at interrupt time). chn_intr() arranges to copy new data to the area that was transferred to the device (now free), and make appropriate updates to the snd_dbuf structure. + + channel_init xxxchannel_init() is called to initialize each of the play or record channels. The calls are initiated from the sound driver attach routine. (See the probe and attach section). static void * xxxchannel_init(kobj_t obj, void *data, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct xxx_info *sc = data; struct xxx_chinfo *ch; ... return ch; } b is the address for the channel struct snd_dbuf. It should be initialized in the function by calling sndbuf_alloc(). The buffer size to use is normally a small multiple of the 'typical' unit transfer size for your device. c is the pcm channel control structure pointer. This is an opaque object. The function should store it in the local channel structure, to be used in later calls to pcm (ie: chn_intr(c)). dir indicates the channel direction (PCMDIR_PLAY or PCMDIR_REC). The function should return a pointer to the private area used to control this channel. This will be passed as a parameter to other channel interface calls. channel_setformat xxxchannel_setformat() should set up the hardware for the specified channel for the specified sound format. static int xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format) { struct xxx_chinfo *ch = data; ... return 0; } format is specified as an AFMT_XXX value (soundcard.h). channel_setspeed xxxchannel_setspeed() sets up the channel hardware for the specified sampling speed, and returns the possibly adjusted speed. static int xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct xxx_chinfo *ch = data; ... return speed; } channel_setblocksize xxxchannel_setblocksize() sets the block size, which is the size of unit transactions between pcm and the sound driver, and between the sound driver and the device. Typically, this would be the number of bytes transferred before an interrupt occurs. During a transfer, the sound driver should call pcm's chn_intr() every time this size has been transferred. Most sound drivers only take note of the block size here, to be used when an actual transfer will be started. static int xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct xxx_chinfo *ch = data; ... return blocksize; } The function returns the possibly adjusted block size. In case the block size is indeed changed, sndbuf_resize() should be called to adjust the buffer. channel_trigger xxxchannel_trigger() is called by pcm to control data transfer operations in the driver. static int xxxchannel_trigger(kobj_t obj, void *data, int go) { struct xxx_chinfo *ch = data; ... return 0; } go defines the action for the current call. The possible values are: PCMTRIG_START: the driver should start a data transfer from or to the channel buffer. If needed, the buffer base and size can be retrieved through sndbuf_getbuf() and sndbuf_getsize(). PCMTRIG_EMLDMAWR / PCMTRIG_EMLDMARD: this tells the driver that the input or output buffer may have been updated. Most drivers just ignore these calls. PCMTRIG_STOP / PCMTRIG_ABORT: the driver should stop the current transfer. If the driver uses ISA DMA, sndbuf_isadma() should be called before performing actions on the device, and will take care of the DMA chip side of things. channel_getptr xxxchannel_getptr() returns the current offset in the transfer buffer. This will typically be called by chn_intr(), and this is how pcm knows where it can transfer new data. channel_free xxxchannel_free() is called to free up channel resources, for example when the driver is unloaded, and should be implemented if the channel data structures are dynamically allocated or if sndbuf_alloc() was not used for buffer allocation. channel_getcaps struct pcmchan_caps * xxxchannel_getcaps(kobj_t obj, void *data) { return &xxx_caps; } The routine returns a pointer to a (usually statically-defined) pcmchan_caps structure (defined in sound/pcm/channel.h. The structure holds the minimum and maximum sampling frequencies, and the accepted sound formats. Look at any sound driver for an example. More functions channel_reset(), channel_resetdone(), and channel_notify() are for special purposes and should not be implemented in a driver without discussing it with the authorities (&a.cg;). channel_setdir() is deprecated. The MIXER interface mixer_init xxxmixer_init() initializes the hardware and tells pcm what mixer devices are available for playing and recording static int xxxmixer_init(struct snd_mixer *m) { struct xxx_info *sc = mix_getdevinfo(m); u_int32_t v; [Initialize hardware] [Set appropriate bits in v for play mixers] mix_setdevs(m, v); [Set appropriate bits in v for record mixers] mix_setrecdevs(m, v) return 0; } Set bits in an integer value and call mix_setdevs() and mix_setrecdevs() to tell pcm what devices exist. Mixer bits definitions can be found in soundcard.h (SOUND_MASK_XXX values and SOUND_MIXER_XXX bit shifts). mixer_set xxxmixer_set() sets the volume level for one mixer device. static int xxxmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct sc_info *sc = mix_getdevinfo(m); [set volume level] return left | (right << 8); } The device is specified as a SOUND_MIXER_XXX value The volume values are specified in range [0-100]. A value of zero should mute the device. As the hardware levels probably won't match the input scale, and some rounding will occur, the routine returns the actual level values (in range 0-100) as shown. mixer_setrecsrc xxxmixer_setrecsrc() sets the recording source device. static int xxxmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct xxx_info *sc = mix_getdevinfo(m); [look for non zero bit(s) in src, set up hardware] [update src to reflect actual action] return src; } The desired recording devices are specified as a bit field The actual devices set for recording are returned. Some drivers can only set one device for recording. The function should return -1 if an error occurs. mixer_uninit, mixer_reinit xxxmixer_uninit() should ensure that all sound is muted and if possible mixer hardware should be powered down xxxmixer_reinit() should ensure that the mixer hardware is powered up and any settings not controlled by mixer_set() or mixer_setrecsrc() are restored. The AC97 interface The AC97 interface is implemented by drivers with an AC97 codec. It only has three methods: xxxac97_init() returns the number of ac97 codecs found. ac97_read() and ac97_write() read or write a specified register. The AC97 interface is used by the AC97 code in pcm to perform higher level operations. Look at sound/pci/maestro3.c or many others under sound/pci/ for an example. diff --git a/en_US.ISO8859-1/books/developers-handbook/boot/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/boot/chapter.sgml index 9e8ae2ef1c..0e28c6519e 100644 --- a/en_US.ISO8859-1/books/developers-handbook/boot/chapter.sgml +++ b/en_US.ISO8859-1/books/developers-handbook/boot/chapter.sgml @@ -1,1023 +1,1024 @@ Sergey Lyubka Contributed by Bootstrapping and kernel initialization Synopsis This chapter is an overview of the boot and system initialization process, starting from the BIOS (firmware) POST, to the first user process creation. Since the initial steps of system startup are very architecture dependent, the IA-32 architecture is used as an example. Overview A computer running FreeBSD can boot by several methods, although the most common method, booting from a harddisk where the OS is installed, will be discussed here. The boot process is divided into several steps: BIOS POST boot0 stage boot2 stage loader stage kernel initialization The boot0 and boot2 stages are also referred to as bootstrap stages 1 and 2 in &man.boot.8; as the first steps in FreeBSD's 3-stage bootstrapping procedure. Various information is printed on the screen at each stage, so you may visually recognize them using the table that follows. Please note that the actual data may differ from machine to machine: may vary BIOS (firmware) messages F1 FreeBSD F2 BSD F5 Disk 2 boot0 >>FreeBSD/i386 BOOT Default: 1:ad(1,a)/boot/loader boot: boot2This prompt will appear if the user presses a key just after selecting an OS to boot at the boot0 stage. BTX loader 1.0 BTX version is 1.01 BIOS drive A: is disk0 BIOS drive C: is disk1 BIOS 639kB/64512kB available memory FreeBSD/i386 bootstrap loader, Revision 0.8 Console internal video/keyboard (jkh@bento.freebsd.org, Mon Nov 20 11:41:23 GMT 2000) /kernel text=0x1234 data=0x2345 syms=[0x4+0x3456] Hit [Enter] to boot immediately, or any other key for command prompt Booting [kernel] in 9 seconds..._ loader Copyright (c) 1992-2002 The FreeBSD Project. Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994 The Regents of the University of California. All rights reserved. FreeBSD 4.6-RC #0: Sat May 4 22:49:02 GMT 2002 devnull@kukas:/usr/obj/usr/src/sys/DEVNULL Timecounter "i8254" frequency 1193182 Hz kernel BIOS POST When the PC powers on, the processor's registers are set to some predefined values. One of the registers is the instruction pointer register, and its value after a power on is well defined: it is a 32-bit value of 0xfffffff0. The instruction pointer register points to code to be executed by the processor. One of the registers is the cr1 32-bit control register, and its value just after the reboot is 0. One of the cr1's bits, the bit PE (Protected Enabled) indicates whether the processor is running in protected or real mode. Since at boot time this bit is cleared, the processor boots in real mode. Real mode means, among other things, that linear and physical addresses are identical. The value of 0xfffffff0 is slightly less then 4Gb, so unless the machine has 4Gb physical memory, it cannot point to a valid memory address. The computer's hardware translates this address so that it points to a BIOS memory block. BIOS stands for Basic Input Output System, and it is a chip on the motherboard that has a relatively small amount of read-only memory (ROM). This memory contains various low-level routines that are specific to the hardware supplied with the motherboard. So, the processor will first jump to the address 0xfffffff0, which really resides in the BIOS's memory. Usually this address contains a jump instruction to the BIOS's POST routines. POST stands for Power On Self Test. This is a set of routines including the memory check, system bus check and other low-level stuff so that the CPU can initialize the computer properly. The important step on this stage is determining the boot device. All modern BIOS's allow the boot device to be set manually, so you can boot from a floppy, CD-ROM, harddisk etc. The very last thing in the POST is the INT 0x19 instruction. That instruction reads 512 bytes from the first sector of boot device into the memory at address 0x7c00. The term first sector originates from harddrive architecture, where the magnetic plate is divided to a number of cylindrical tracks. Tracks are numbered, and every track is divided by a number (usually 64) sectors. Track number 0 is the outermost on the magnetic plate, and sector 1, the first sector (tracks, or, cylinders, are numbered starting from 0, but sectors - starting from 1), has a special meaning. It is also called Master Boot Record, or MBR. The remaining sectors on the first track are never used Some utilities such as &man.disklabel.8; may store the information in this area, mostly in the second sector.. <literal>boot0</literal> stage Take a look at the file /boot/boot0. This is a small 512-byte file, and it is exactly what FreeBSD's installation procedure wrote to your harddisk's MBR if you chose the bootmanager option at installation time. As mentioned previously, the INT 0x19 instruction loads an MBR, i.e. the boot0 content, into the memory at address 0x7c00. Taking a look at the file sys/boot/i386/boot0/boot0.s can give a guess at what is happening there - this is the boot manager, which is an awesome piece of code written by Robert Nordier. The MBR, or, boot0, has a special structure starting from offset 0x1be, called the partition table. It has 4 records of 16 bytes each, called partition records, which represent how the harddisk(s) are partitioned, or, in FreeBSD's terminology, sliced. One byte of those 16 says whether a partition (slice) is bootable or not. Exactly one record must have that flag set, otherwise boot0's code will refuse to proceed. A partition record has the following fields: the 1-byte filesystem type the 1-byte bootable flag the 6 byte descriptor in CHS format the 8 byte descriptor in LBA format A partition record descriptor has the information about where exactly the partition resides on the drive. Both descriptors, LBA and CHS, describe the same information, but in different ways: LBA (Logical Block Addressing) has the starting sector for the partition and the partition's length, while CHS (Cylinder Head Sector) has coordinates for the first and last sectors of the partition. The boot manager scans the partition table and prints the menu on the screen so the user can select what disk and what slice to boot. By pressing an appropriate key, boot0 performs the following actions: modifies the bootable flag for the selected partition to make it bootable, and clears the previous saves itself to disk to remember what partition (slice) has been selected so to use it as the default on the next boot loads the first sector of the selected partition (slice) into memory and jumps there What kind of data should reside on the very first sector of a bootable partition (slice), in our case, a FreeBSD slice? As you may have already guessed, it is boot2. <literal>boot2</literal> stage You might wonder, why boot2 comes after boot0, and not boot1. Actually, there is a 512-byte file called boot1 in the directory /boot as well. It is used for booting from a floppy. When booting from a floppy, boot1 plays the same role as boot0 for a harddisk: it locates boot2 and runs it. You may have realized that a file /boot/mbr exists as well. It is a simplified version of boot0. The code in mbr does not provide a menu for the user, it just blindly boots the partition marked active. The code implementing boot2 resides in sys/boot/i386/boot2/, and the executable itself is in /boot. The files boot0 and boot2 that are in /boot are not used by the bootstrap, but by utilities such as boot0cfg. The actual position for boot0 is in the MBR. For boot2 it is the beginning of a bootable FreeBSD slice. These locations are not under the filesystem's control, so they are invisible to commands like ls. The main task for boot2 is to load the file /boot/loader, which is the third stage in the bootstrapping procedure. The code in boot2 cannot use any services like open() and read(), since the kernel is not yet loaded. It must scan the harddisk, knowing about the filesystem structure, find the file /boot/loader, read it into memory using a BIOS service, and then pass the execution to the loader's entry point. Besides that, boot2 prompts for user input so the loader can be booted from different disk, unit, slice and partition. The boot2 binary is created in special way: sys/boot/i386/boot2/Makefile boot2: boot2.ldr boot2.bin ${BTX}/btx/btx btxld -v -E ${ORG2} -f bin -b ${BTX}/btx/btx -l boot2.ldr \ -o boot2.ld -P 1 boot2.bin This Makefile snippet shows that &man.btxld.8; is used to link the binary. BTX, which stands for BooT eXtender, is a piece of code that provides a protected mode environment for the program, called the client, that it is linked with. So boot2 is a BTX client, i.e. it uses the service provided by BTX. The btxld utility is the linker. It links two binaries together. The difference between &man.btxld.8; and &man.ld.1; is that ld usually links object files into a shared object or executable, while btxld links an object file with the BTX, producing the binary file suitable to be put on the beginning of the partition for the system boot. boot0 passes the execution to BTX's entry point. BTX then switches the processor to protected mode, and prepares a simple environment before calling the client. This includes: virtual v86 mode. That means, the BTX is a v86 monitor. Real mode instructions like posh, popf, cli, sti, if called by the client, will work. Interrupt Descriptor Table (IDT) is set up so all hardware interrupts are routed to the default BIOS's handlers, and interrupt 0x30 is set up to be the syscall gate. Two system calls: exec and exit, are defined: sys/boot/i386/btx/lib/btxsys.s: .set INT_SYS,0x30 # Interrupt number # # System call: exit # __exit: xorl %eax,%eax # BTX system int $INT_SYS # call 0x0 # # System call: exec # __exec: movl $0x1,%eax # BTX system int $INT_SYS # call 0x1 BTX creates a Global Descriptor Table (GDT): sys/boot/i386/btx/btx/btx.s: gdt: .word 0x0,0x0,0x0,0x0 # Null entry .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS The client's code and data start from address MEM_USR (0xa000), and a selector (SEL_UCODE) points to the client's code segment. The SEL_UCODE descriptor has Descriptor Privilege Level (DPL) 3, which is the lowest privilege level. But the INT 0x30 instruction handler resides in a segment pointed to by the SEL_SCODE (supervisor code) selector, as shown from the code that creates an IDT: mov $SEL_SCODE,%dh # Segment selector init.2: shr %bx # Handle this int? jnc init.3 # No mov %ax,(%di) # Set handler offset mov %dh,0x2(%di) # and selector mov %dl,0x5(%di) # Set P:DPL:type add $0x4,%ax # Next handler So, when the client calls __exec(), the code will be executed with the highest privileges. This allows the kernel to change the protected mode data structures, such as page tables, GDT, IDT, etc later, if needed. boot2 defines an important structure, struct bootinfo. This structure is initialized by boot2 and passed to the loader, and then further to the kernel. Some nodes of this structures are set by boot2, the rest by the loader. This structure, among other information, contains the kernel filename, BIOS harddisk geometry, BIOS drive number for boot device, physical memory available, envp pointer etc. The definition for it is: /usr/include/machine/bootinfo.h struct bootinfo { u_int32_t bi_version; u_int32_t bi_kernelname; /* represents a char * */ u_int32_t bi_nfs_diskless; /* struct nfs_diskless * */ /* End of fields that are always present. */ #define bi_endcommon bi_n_bios_used u_int32_t bi_n_bios_used; u_int32_t bi_bios_geom[N_BIOS_GEOM]; u_int32_t bi_size; u_int8_t bi_memsizes_valid; u_int8_t bi_bios_dev; /* bootdev BIOS unit number */ u_int8_t bi_pad[2]; u_int32_t bi_basemem; u_int32_t bi_extmem; u_int32_t bi_symtab; /* struct symtab * */ u_int32_t bi_esymtab; /* struct symtab * */ /* Items below only from advanced bootloader */ u_int32_t bi_kernend; /* end of kernel space */ u_int32_t bi_envp; /* environment */ u_int32_t bi_modulep; /* preloaded modules */ }; boot2 enters into an infinite loop waiting for user input, then calls load(). If the user does not press anything, the loop brakes by a timeout, so load() will load the default file (/boot/loader). Functions ino_t lookup(char *filename) and int xfsread(ino_t inode, void *buf, size_t nbyte) are used to read the content of a file into memory. /boot/loader is an ELF binary, but where the ELF header is prepended with a.out's struct exec structure. load() scans the loader's ELF header, loading the content of /boot/loader into memory, and passing the execution to the loader's entry: sys/boot/i386/boot2/boot2.c: __exec((caddr_t)addr, RB_BOOTINFO | (opts & RBX_MASK), MAKEBOOTDEV(dev_maj[dsk.type], 0, dsk.slice, dsk.unit, dsk.part), 0, 0, 0, VTOP(&bootinfo)); <application>loader</application> stage loader is a BTX client as well. I will not describe it here in detail, there is a comprehensive manpage written by Mike Smith, &man.loader.8;. The underlying mechanisms and BTX were discussed above. The main task for the loader is to boot the kernel. When the kernel is loaded into memory, it is being called by the loader: sys/boot/common/boot.c: /* Call the exec handler from the loader matching the kernel */ module_formats[km->m_loader]->l_exec(km); Kernel initialization To where exactly is the execution passed by the loader, i.e. what is the kernel's actual entry point. Let us take a look at the command that links the kernel: sys/conf/Makefile.i386: ld -elf -Bdynamic -T /usr/src/sys/conf/ldscript.i386 -export-dynamic \ -dynamic-linker /red/herring -o kernel -X locore.o \ <lots of kernel .o files> A few interesting things can be seen in this line. First, the kernel is an ELF dynamically linked binary, but the dynamic linker for kernel is /red/herring, which is definitely a bogus file. Second, taking a look at the file sys/conf/ldscript.i386 gives an idea about what ld options are used when compiling a kernel. Reading through the first few lines, the string sys/conf/ldscript.i386: ENTRY(btext) says that a kernel's entry point is the symbol `btext'. This symbol is defined in locore.s: sys/i386/i386/locore.s: .text /********************************************************************** * * This is where the bootblocks start us, set the ball rolling... * */ NON_GPROF_ENTRY(btext) First what is done is the register EFLAGS is set to a predefined value of 0x00000002, and then all the segment registers are initialized: sys/i386/i386/locore.s /* Don't trust what the BIOS gives for eflags. */ pushl $PSL_KERNEL popfl /* * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap * to set %cs, %ds, %es and %ss. */ mov %ds, %ax mov %ax, %fs mov %ax, %gs btext calls the routines recover_bootinfo(), identify_cpu(), create_pagetables(), which are also defined in locore.s. Here is a description of what they do: recover_bootinfo This routine parses the parameters to the kernel passed from the bootstrap. The kernel may have been booted in 3 ways: by the loader, described above, by the old disk boot blocks, and by the old diskless boot procedure. This function determines the booting method, and stores the struct bootinfo structure into the kernel memory. identify_cpu This functions tries to find out what CPU it is running on, storing the value found in a variable _cpu. create_pagetables This function allocates and fills out a Page Table Directory at the top of the kernel memory area. + The next steps are enabling VME, if the CPU supports it: testl $CPUID_VME, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_VME, %eax movl %eax, %cr4 Then, enabling paging: /* Now enable paging */ movl R(_IdlePTD), %eax movl %eax,%cr3 /* load ptd addr into mmu */ movl %cr0,%eax /* get control word */ orl $CR0_PE|CR0_PG,%eax /* enable paging */ movl %eax,%cr0 /* and let's page NOW! */ The next three lines of code are because the paging was set, so the jump is needed to continue the execution in virtualized address space: pushl $begin /* jump to high virtualized address */ ret /* now running relocated at KERNBASE where the system is linked to run */ begin: The function init386() is called, with a pointer to the first free physical page, after that mi_startup(). init386 is an architecture dependent initialization function, and mi_startup() is an architecture independent one (the 'mi_' prefix stands for Machine Independent). The kernel never returns from mi_startup(), and by calling it, the kernel finishes booting: sys/i386/i386/locore.s: movl physfree, %esi pushl %esi /* value of first for init386(first) */ call _init386 /* wire 386 chip for unix operation */ call _mi_startup /* autoconfiguration, mountroot etc */ hlt /* never returns to here */ <function>init386()</function> init386() is defined in sys/i386/i386/machdep.c and performs low-level initialization, specific to the i386 chip. The switch to protected mode was performed by the loader. The loader has created the very first task, in which the kernel continues to operate. Before running straight away to the code, I will enumerate the tasks the processor must complete to initialize protected mode execution: Initialize the kernel tunable parameters, passed from the bootstrapping program. Prepare the GDT. Prepare the IDT. Initialize the system console. Initialize the DDB, if it is compiled into kernel. Initialize the TSS. Prepare the LDT. Setup proc0's pcb. What init386() first does is initialize the tunable parameters passed from bootstrap. This is done by setting the environment pointer (envp) and calling init_param1(). The envp pointer has been passed from loader in the bootinfo structure: sys/i386/i386/machdep.c: kern_envp = (caddr_t)bootinfo.bi_envp + KERNBASE; /* Init basic tunables, hz etc */ init_param1(); init_param1() is defined in sys/kern/subr_param.c. That file has a number of sysctls, and two functions, init_param1() and init_param2(), that are called from init386(): sys/kern/subr_param.c hz = HZ; TUNABLE_INT_FETCH("kern.hz", &hz); TUNABLE_<typename>_FETCH is used to fetch the value from the environment: /usr/src/sys/sys/kernel.h #define TUNABLE_INT_FETCH(path, var) getenv_int((path), (var)) Sysctl kern.hz is the system clock tick. Along with this, the following sysctls are set by init_param1(): kern.maxswzone, kern.maxbcache, kern.maxtsiz, kern.dfldsiz, kern.dflssiz, kern.maxssiz, kern.sgrowsiz. Then init386() prepares the Global Descriptors Table (GDT). Every task on an x86 is running in its own virtual address space, and this space is addressed by a segment:offset pair. Say, for instance, the current instruction to be executed by the processor lies at CS:EIP, then the linear virtual address for that instruction would be the virtual address of code segment CS + EIP. For convenience, segments begin at virtual address 0 and end at a 4Gb boundary. Therefore, the instruction's linear virtual address for this example would just be the value of EIP. Segment registers such as CS, DS etc are the selectors, i.e. indexes, into GDT (to be more precise, an index is not a selector itself, but the INDEX field of a selector). FreeBSD's GDT holds descriptors for 15 selectors per CPU: sys/i386/i386/machdep.c: union descriptor gdt[NGDT * MAXCPU]; /* global descriptor table */ sys/i386/include/segments.h: /* * Entries in the Global Descriptor Table (GDT) */ #define GNULL_SEL 0 /* Null Descriptor */ #define GCODE_SEL 1 /* Kernel Code Descriptor */ #define GDATA_SEL 2 /* Kernel Data Descriptor */ #define GPRIV_SEL 3 /* SMP Per-Processor Private Data */ #define GPROC0_SEL 4 /* Task state process slot zero and up */ #define GLDT_SEL 5 /* LDT - eventually one per process */ #define GUSERLDT_SEL 6 /* User LDT */ #define GTGATE_SEL 7 /* Process task switch gate */ #define GBIOSLOWMEM_SEL 8 /* BIOS low memory access (must be entry 8) */ #define GPANIC_SEL 9 /* Task state to consider panic from */ #define GBIOSCODE32_SEL 10 /* BIOS interface (32bit Code) */ #define GBIOSCODE16_SEL 11 /* BIOS interface (16bit Code) */ #define GBIOSDATA_SEL 12 /* BIOS interface (Data) */ #define GBIOSUTIL_SEL 13 /* BIOS interface (Utility) */ #define GBIOSARGS_SEL 14 /* BIOS interface (Arguments) */ Note that those #defines are not selectors themselves, but just a field INDEX of a selector, so they are exactly the indices of the GDT. for example, an actual selector for the kernel code (GCODE_SEL) has the value 0x08. The next step is to initialize the Interrupt Descriptor Table (IDT). This table is to be referenced by the processor when a software or hardware interrupt occurs. For example, to make a system call, user application issues the INT 0x80 instruction. This is a software interrupt, so the processor's hardware looks up a record with index 0x80 in the IDT. This record points to the routine that handles this interrupt, in this particular case, this will be the kernel's syscall gate. The IDT may have a maximum of 256 (0x100) records. The kernel allocates NIDT records for the IDT, where NIDT is the maximum (256): sys/i386/i386/machdep.c: static struct gate_descriptor idt0[NIDT]; struct gate_descriptor *idt = &idt0[0]; /* interrupt descriptor table */ For each interrupt, an appropriate handler is set. The syscall gate for INT 0x80 is set as well: sys/i386/i386/machdep.c: setidt(0x80, &IDTVEC(int0x80_syscall), SDT_SYS386TGT, SEL_UPL, GSEL(GCODE_SEL, SEL_KPL)); So when a userland application issues the INT 0x80 instruction, control will transfer to the function _Xint0x80_syscall, which is in the kernel code segment and will be executed with supervisor privileges. Console and DDB are then initialized: sys/i386/i386/machdep.c: cninit(); /* skipped */ #ifdef DDB kdb_init(); if (boothowto & RB_KDB) Debugger("Boot flags requested debugger"); #endif The Task State Segment is another x86 protected mode structure, the TSS is used by the hardware to store task information when a task switch occurs. The Local Descriptors Table is used to reference userland code and data. Several selectors are defined to point to the LDT, they are the system call gates and the user code and data selectors: /usr/include/machine/segments.h #define LSYS5CALLS_SEL 0 /* forced by intel BCS */ #define LSYS5SIGR_SEL 1 #define L43BSDCALLS_SEL 2 /* notyet */ #define LUCODE_SEL 3 #define LSOL26CALLS_SEL 4 /* Solaris >= 2.6 system call gate */ #define LUDATA_SEL 5 /* separate stack, es,fs,gs sels ? */ /* #define LPOSIXCALLS_SEL 5*/ /* notyet */ #define LBSDICALLS_SEL 16 /* BSDI system call gate */ #define NLDT (LBSDICALLS_SEL + 1) Next, proc0's Process Control Block (struct pcb) structure is initialized. proc0 is a struct proc structure that describes a kernel process. It is always present while the kernel is running, therefore it is declared as global: sys/kern/kern_init.c: struct proc proc0; The structure struct pcb is a part of a proc structure. It is defined in /usr/include/machine/pcb.h and has a process's information specific to the i386 architecture, such as registers values. <function>mi_startup()</function> This function performs a bubble sort of all the system initialization objects and then calls the entry of each object one by one: sys/kern/init_main.c: for (sipp = sysinit; *sipp; sipp++) { /* ... skipped ... */ /* Call function */ (*((*sipp)->func))((*sipp)->udata); /* ... skipped ... */ } Although the sysinit framework is described in the Developers' Handbook, I will discuss the internals of it. Every system initialization object (sysinit object) is created by calling a SYSINIT() macro. Let us take as example an announce sysinit object. This object prints the copyright message: sys/kern/init_main.c: static void print_caddr_t(void *data __unused) { printf("%s", (char *)data); } SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_caddr_t, copyright) The subsystem ID for this object is SI_SUB_COPYRIGHT (0x0800001), which comes right after the SI_SUB_CONSOLE (0x0800000). So, the copyright message will be printed out first, just after the console initialization. Let us take a look at what exactly the macro SYSINIT() does. It expands to a C_SYSINIT() macro. The C_SYSINIT() macro then expands to a static struct sysinit structure declaration with another DATA_SET macro call: /usr/include/sys/kernel.h: #define C_SYSINIT(uniquifier, subsystem, order, func, ident) \ static struct sysinit uniquifier ## _sys_init = { \ subsystem, \ order, \ func, \ ident \ }; \ DATA_SET(sysinit_set,uniquifier ## _sys_init); #define SYSINIT(uniquifier, subsystem, order, func, ident) \ C_SYSINIT(uniquifier, subsystem, order, \ (sysinit_cfunc_t)(sysinit_nfunc_t)func, (void *)ident) The DATA_SET() macro expands to a MAKE_SET(), and that macro is the point where the all sysinit magic is hidden: /usr/include/linker_set.h #define MAKE_SET(set, sym) \ static void const * const __set_##set##_sym_##sym = &sym; \ __asm(".section .set." #set ",\"aw\""); \ __asm(".long " #sym); \ __asm(".previous") #endif #define TEXT_SET(set, sym) MAKE_SET(set, sym) #define DATA_SET(set, sym) MAKE_SET(set, sym) In our case, the following declaration will occur: static struct sysinit announce_sys_init = { SI_SUB_COPYRIGHT, SI_ORDER_FIRST, (sysinit_cfunc_t)(sysinit_nfunc_t) print_caddr_t, (void *) copyright }; static void const *const __set_sysinit_set_sym_announce_sys_init = &announce_sys_init; __asm(".section .set.sysinit_set" ",\"aw\""); __asm(".long " "announce_sys_init"); __asm(".previous"); The first __asm instruction will create an ELF section within the kernel's executable. This will happen at kernel link time. The section will have the name .set.sysinit_set. The content of this section is one 32-bit value, the address of announce_sys_init structure, and that is what the second __asm is. The third __asm instruction marks the end of a section. If a directive with the same section name occurred before, the content, i.e. the 32-bit value, will be appended to the existing section, so forming an array of 32-bit pointers. Running objdump on a kernel binary, you may notice the presence of such small sections: &prompt.user; objdump -h /kernel 7 .set.cons_set 00000014 c03164c0 c03164c0 002154c0 2**2 CONTENTS, ALLOC, LOAD, DATA 8 .set.kbddriver_set 00000010 c03164d4 c03164d4 002154d4 2**2 CONTENTS, ALLOC, LOAD, DATA 9 .set.scrndr_set 00000024 c03164e4 c03164e4 002154e4 2**2 CONTENTS, ALLOC, LOAD, DATA 10 .set.scterm_set 0000000c c0316508 c0316508 00215508 2**2 CONTENTS, ALLOC, LOAD, DATA 11 .set.sysctl_set 0000097c c0316514 c0316514 00215514 2**2 CONTENTS, ALLOC, LOAD, DATA 12 .set.sysinit_set 00000664 c0316e90 c0316e90 00215e90 2**2 CONTENTS, ALLOC, LOAD, DATA This screen dump shows that the size of .set.sysinit_set section is 0x664 bytes, so 0x664/sizeof(void *) sysinit objects are compiled into the kernel. The other sections such as .set.sysctl_set represent other linker sets. By defining a variable of type struct linker_set the content of .set.sysinit_set section will be collected into that variable: sys/kern/init_main.c: extern struct linker_set sysinit_set; /* XXX */ The struct linker_set is defined as follows: /usr/include/linker_set.h: struct linker_set { int ls_length; void *ls_items[1]; /* really ls_length of them, trailing NULL */ }; The first node will be equal to the number of a sysinit objects, and the second node will be a NULL-terminated array of pointers to them. Returning to the mi_startup() discussion, it is must be clear now, how the sysinit objects are being organized. The mi_startup() function sorts them and calls each. The very last object is the system scheduler: /usr/include/sys/kernel.h: enum sysinit_sub_id { SI_SUB_DUMMY = 0x0000000, /* not executed; for linker*/ SI_SUB_DONE = 0x0000001, /* processed*/ SI_SUB_CONSOLE = 0x0800000, /* console*/ SI_SUB_COPYRIGHT = 0x0800001, /* first use of console*/ ... SI_SUB_RUN_SCHEDULER = 0xfffffff /* scheduler: no return*/ }; The system scheduler sysinit object is defined in the file sys/vm/vm_glue.c, and the entry point for that object is scheduler(). That function is actually an infinite loop, and it represents a process with PID 0, the swapper process. The proc0 structure, mentioned before, is used to describe it. The first user process, called init, is created by the sysinit object init: sys/kern/init_main.c: static void create_init(const void *udata __unused) { int error; int s; s = splhigh(); error = fork1(&proc0, RFFDG | RFPROC, &initproc); if (error) panic("cannot fork init: %d\n", error); initproc->p_flag |= P_INMEM | P_SYSTEM; cpu_set_fork_handler(initproc, start_init, NULL); remrunqueue(initproc); splx(s); } SYSINIT(init,SI_SUB_CREATE_INIT, SI_ORDER_FIRST, create_init, NULL) The create_init() allocates a new process by calling fork1(), but does not mark it runnable. When this new process is scheduled for execution by the scheduler, the start_init() will be called. That function is defined in init_main.c. It tries to load and exec the init binary, probing /sbin/init first, then /sbin/oinit, /sbin/init.bak, and finally /stand/sysinstall: sys/kern/init_main.c: static char init_path[MAXPATHLEN] = #ifdef INIT_PATH __XSTRING(INIT_PATH); #else "/sbin/init:/sbin/oinit:/sbin/init.bak:/stand/sysinstall"; #endif diff --git a/en_US.ISO8859-1/books/developers-handbook/jail/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/jail/chapter.sgml index f792cbd338..5bf43c7833 100644 --- a/en_US.ISO8859-1/books/developers-handbook/jail/chapter.sgml +++ b/en_US.ISO8859-1/books/developers-handbook/jail/chapter.sgml @@ -1,597 +1,597 @@ Evan Sarmiento
evms@cs.bu.edu
2001 Evan Sarmiento
The Jail Subsystem On most UNIX systems, root has omnipotent power. This promotes insecurity. If an attacker were to gain root on a system, he would have every function at his fingertips. In FreeBSD there are sysctls which dilute the power of root, in order to minimize the damage caused by an attacker. Specifically, one of these functions is called secure levels. Similarly, another function which is present from FreeBSD 4.0 and onward, is a utility called &man.jail.8;. Jail chroots an environment and sets certain restrictions on processes which are forked from within. For example, a jailed process cannot affect processes outside of the jail, utilize certain system calls, or inflict any damage on the main computer. Jail is becoming the new security model. People are running potentially vulnerable servers such as Apache, BIND, and sendmail within jails, so that if an attacker gains root within the Jail, it is only an annoyance, and not a devastation. This article focuses on the internals (source code) of Jail. It will also suggest improvements upon the jail code base which are already being worked on. If you are looking for a how-to on setting up a Jail, I suggest you look at my other article in Sys Admin Magazine, May 2001, entitled "Securing FreeBSD using Jail." Architecture Jail consists of two realms: the user-space program, jail, and the code implemented within the kernel: the jail() system call and associated restrictions. I will be discussing the user-space program and then how jail is implemented within the kernel. Userland code The source for the user-land jail is located in /usr/src/usr.sbin/jail, consisting of one file, jail.c. The program takes these arguments: the path of the jail, hostname, ip address, and the command to be executed. Data Structures In jail.c, the first thing I would note is the declaration of an important structure struct jail j; which was included from - /usr/include/sys/jail.h. + /usr/include/sys/jail.h. The definition of the jail structure is: /usr/include/sys/jail.h: struct jail { u_int32_t version; char *path; char *hostname; u_int32_t ip_number; }; As you can see, there is an entry for each of the arguments passed to the jail program, and indeed, they are set during it's execution. /usr/src/usr.sbin/jail.c j.version = 0; j.path = argv[1]; j.hostname = argv[2]; Networking One of the arguments passed to the Jail program is an IP address with which the jail can be accessed over the network. Jail translates the ip address given into network byte order and then stores it in j (the jail structure). /usr/src/usr.sbin/jail/jail.c: struct in.addr in; ... i = inet.aton(argv[3], ); ... j.ip_number = ntohl(in.s.addr); The inet_aton3 function "interprets the specified character string as an Internet address, placing the address into the structure provided." The ip number node in the jail structure is set only when the ip address placed onto the in structure by inet aton is translated into network byte order by ntohl(). Jailing The Process Finally, the userland program jails the process, and executes the command specified. Jail now becomes an imprisoned process itself and forks a child process which - then executes the command given using &man.execv.3; + then executes the command given using &man.execv.3; /usr/src/sys/usr.sbin/jail/jail.c i = jail(); ... i = execv(argv[4], argv + 4); As you can see, the jail function is being called, and its argument is the jail structure which has been filled with the arguments given to the program. Finally, the program you specify is executed. I will now discuss how Jail is implemented within the kernel. Kernel Space We will now be looking at the file /usr/src/sys/kern/kern_jail.c. This is the file where the jail system call, appropriate sysctls, and networking functions are defined. sysctls In kern_jail.c, the following sysctls are defined: /usr/src/sys/kern/kern_jail.c: int jail_set_hostname_allowed = 1; SYSCTL_INT(_jail, OID_AUTO, set_hostname_allowed, CTLFLAG_RW, _set_hostname_allowed, 0, "Processes in jail can set their hostnames"); int jail_socket_unixiproute_only = 1; SYSCTL_INT(_jail, OID_AUTO, socket_unixiproute_only, CTLFLAG_RW, _socket_unixiproute_only, 0, "Processes in jail are limited to creating UNIX/IPv4/route sockets only "); int jail_sysvipc_allowed = 0; SYSCTL_INT(_jail, OID_AUTO, sysvipc_allowed, CTLFLAG_RW, _sysvipc_allowed, 0, "Processes in jail can use System V IPC primitives"); Each of these sysctls can be accessed by the user through the sysctl program. Throughout the kernel, these specific sysctls are recognized by their name. For example, the name of the first sysctl is jail.set.hostname.allowed. &man.jail.2; system call Like all system calls, the &man.jail.2; system call takes two arguments, struct proc *p and struct jail_args *uap. p is a pointer to a proc structure which describes the calling process. In this context, uap is a pointer to a structure which specifies the arguments given to &man.jail.2; from the userland program jail.c. When I described the userland program before, you saw that the &man.jail.2; system call was given a jail structure as its own argument. /usr/src/sys/kern/kern_jail.c: int jail(p, uap) struct proc *p; struct jail_args /* { syscallarg(struct jail *) jail; } */ *uap; Therefore, uap->jail would access the jail structure which was passed to the system call. Next, the system call copies the jail structure into kernel space using the copyin() function. copyin() takes three arguments: the data which is to be copied into kernel space, uap->jail, where to store it, j and the size of the storage. The jail structure uap->jail is copied into kernel space and stored in another jail structure, j. /usr/src/sys/kern/kern_jail.c: error = copyin(uap->jail, , sizeof j); There is another important structure defined in jail.h. It is the prison structure (pr). The prison structure is used exclusively within kernel space. The &man.jail.2; system call copies everything from the jail structure onto the prison structure. Here is the definition of the prison structure. /usr/include/sys/jail.h: struct prison { int pr_ref; char pr_host[MAXHOSTNAMELEN]; u_int32_t pr_ip; void *pr_linux; }; The jail() system call then allocates memory for a pointer to a prison structure and copies data between the two structures. /usr/src/sys/kern/kern_jail.c: MALLOC(pr, struct prison *, sizeof *pr , M_PRISON, M_WAITOK); bzero((caddr_t)pr, sizeof *pr); error = copyinstr(j.hostname, pr_host]]>, sizeof pr->pr_host, 0); if (error) goto bail; Finally, the jail system call chroots the path specified. The chroot function is given two arguments. The first is p, which represents the calling process, the second is a pointer to the structure chroot args. The structure chroot args contains the path which is to be chrooted. As you can see, the path specified in the jail structure is copied to the chroot args structure and used. /usr/src/sys/kern/kern_jail.c: ca.path = j.path; error = chroot(p, ); These next three lines in the source are very important, as they specify how the kernel recognizes a process as jailed. Each process on a Unix system is described by its own proc structure. You can see the whole proc structure in /usr/include/sys/proc.h. For example, the p argument in any system call is actually a pointer to that process' proc structure, as stated before. The proc structure contains nodes which can describe the owner's identity (p_cred), the process resource limits (p_limit), and so on. In the definition of the process structure, there is a pointer to a prison structure. (p_prison). /usr/include/sys/proc.h: struct proc { ... struct prison *p_prison; ... }; In kern_jail.c, the function then copies the pr structure, which is filled with all the information from the original jail structure, over to the p->p_prison structure. It then does a bitwise OR of p->p_flag with the constant P_JAILED, meaning that the calling process is now recognized as jailed. The parent process of each process, forked within the jail, is the program jail itself, as it calls the &man.jail.2; system call. When the program is executed through execve, it inherits the properties of its parents proc structure, therefore it has the p->p_flag set, and the p->p_prison structure is filled. /usr/src/sys/kern/kern_jail.c p->p.prison = pr; p->p.flag |= P.JAILED; When a process is forked from a parent process, the &man.fork.2; system call deals differently with imprisoned processes. In the fork system call, there are two pointers to a proc structure p1 and p2. p1 points to the parent's proc structure and p2 points to the child's unfilled proc structure. After copying all relevant data between the structures, &man.fork.2; checks if the structure p->p_prison is filled on p2. If it is, it increments the pr.ref by one, and sets the p_flag to one on the child process. /usr/src/sys/kern/kern_fork.c: if (p2->p_prison) { p2->p_prison->pr_ref++; p2->p_flag |= P_JAILED; } Restrictions Throughout the kernel there are access restrictions relating to jailed processes. Usually, these restrictions only check if the process is jailed, and if so, returns an error. For example: if (p->p_prison) return EPERM; SysV IPC System V IPC is based on messages. Processes can send each other these messages which tell them how to act. The functions which deal with messages are: msgsys, msgctl, msgget, msgsend and msgrcv. Earlier, I mentioned that there were certain sysctls you could turn on or off in order to affect the behavior of Jail. One of these sysctls was jail_sysvipc_allowed. On most systems, this sysctl is set to 0. If it were set to 1, it would defeat the whole purpose of having a jail; privleged users from within the jail would be able to affect processes outside of the environment. The difference between a message and a signal is that the message only consists of the signal number. /usr/src/sys/kern/sysv_msg.c: &man.msgget.3;: msgget returns (and possibly creates) a message descriptor that designates a message queue for use in other system calls. &man.msgctl.3;: Using this function, a process can query the status of a message descriptor. &man.msgsnd.3;: msgsnd sends a message to a process. &man.msgrcv.3;: a process receives messages using this function In each of these system calls, there is this conditional: /usr/src/sys/kern/sysv msg.c: if (!jail.sysvipc.allowed && p->p_prison != NULL) return (ENOSYS); Semaphore system calls allow processes to synchronize execution by doing a set of operations atomically on a set of semaphores. Basically semaphores provide another way for processes lock resources. However, process waiting on a semaphore, that is being used, will sleep until the resources are relinquished. The following semaphore system calls are blocked inside a jail: semsys, semget, semctl and semop. /usr/src/sys/kern/sysv_sem.c: &man.semctl.2;(id, num, cmd, arg): Semctl does the specified cmd on the semaphore queue indicated by id. &man.semget.2;(key, nsems, flag): Semget creates an array of semaphores, corresponding to key. Key and flag take on the same meaning as they do in msgget. &man.semop.2;(id, ops, num): Semop does the set of semaphore operations in the array of structures ops, to the set of semaphores identified by id. System V IPC allows for processes to share memory. Processes can communicate directly with each other by sharing parts of their virtual address space and then reading and writing data stored in the shared memory. These system calls are blocked within a jailed environment: shmdt, shmat, oshmctl, shmctl, shmget, and shmsys. /usr/src/sys/kern/sysv shm.c: &man.shmctl.2;(id, cmd, buf): shmctl does various control operations on the shared memory region identified by id. &man.shmget.2;(key, size, flag): shmget accesses or creates a shared memory region of size bytes. &man.shmat.2;(id, addr, flag): shmat attaches a shared memory region identified by id to the address space of a process. &man.shmdt.2;(addr): shmdt detaches the shared memory region previously attached at addr. Sockets Jail treats the &man.socket.2; system call and related lower-level socket functions in a special manner. In order to determine whether a certain socket is allowed to be created, it first checks to see if the sysctl jail.socket.unixiproute.only is set. If set, sockets are only allowed to be created if the family specified is either PF_LOCAL, PF_INET or PF_ROUTE. Otherwise, it returns an error. /usr/src/sys/kern/uipc_socket.c: int socreate(dom, aso, type, proto, p) ... register struct protosw *prp; ... { if (p->p_prison && jail_socket_unixiproute_only && prp->pr_domain->dom_family != PR_LOCAL && prp->pr_domain->dom_family != PF_INET && prp->pr_domain->dom_family != PF_ROUTE) return (EPROTONOSUPPORT); ... } Berkeley Packet Filter The Berkeley Packet Filter provides a raw interface to data link layers in a protocol independent fashion. The function bpfopen() opens an Ethernet device. There is a conditional which disallows any jailed processes from accessing this function. /usr/src/sys/net/bpf.c: static int bpfopen(dev, flags, fmt, p) ... { if (p->p_prison) return (EPERM); ... } Protocols There are certain protocols which are very common, such as TCP, UDP, IP and ICMP. IP and ICMP are on the same level: the network layer 2. There are certain precautions which are taken in order to prevent a jailed process from binding a protocol to a certain port only if the nam parameter is set. nam is a pointer to a sockaddr structure, which describes the address on which to bind the service. A more exact definition is that sockaddr "may be used as a template for reffering to the identifying tag and length of each address"[2]. In the function in pcbbind, sin is a pointer to a sockaddr.in structure, which contains the port, address, length and domain family of the socket which is to be bound. Basically, this disallows any processes from jail to be able to specify the domain family. /usr/src/sys/kern/netinet/in_pcb.c: int in.pcbbind(int, nam, p) ... struct sockaddr *nam; struct proc *p; { ... struct sockaddr.in *sin; ... if (nam) { sin = (struct sockaddr.in *)nam; ... if (sin->sin_addr.s_addr != INADDR_ANY) if (prison.ip(p, 0, ->sin.addr.s_addr)) return (EINVAL); .... } ... } You might be wondering what function prison_ip() does. prison.ip is given three arguments, the current process (represented by p), any flags, and an ip address. It returns 1 if the ip address belongs to a jail or 0 if it does not. As you can see from the code, if it is indeed an ip address belonging to a jail, the protcol is not allowed to bind to a certain port. /usr/src/sys/kern/kern_jail.c: int prison_ip(struct proc *p, int flag, u_int32_t *ip) { u_int32_t tmp; if (!p->p_prison) return (0); if (flag) tmp = *ip; else tmp = ntohl (*ip); if (tmp == INADDR_ANY) { if (flag) *ip = p->p_prison->pr_ip; else *ip = htonl(p->p_prison->pr_ip); return (0); } if (p->p_prison->pr_ip != tmp) return (1); return (0); } Jailed users are not allowed to bind services to an ip which does not belong to the jail. The restriction is also written within the function in_pcbbind: /usr/src/sys/net inet/in_pcb.c if (nam) { ... lport = sin->sin.port; ... if (lport) { ... if (p && p->p_prison) prison = 1; if (prison && prison_ip(p, 0, ->sin_addr.s_addr)) return (EADDRNOTAVAIL); Filesystem Even root users within the jail are not allowed to set any file flags, such as immutable, append, and no unlink flags, if the securelevel is greater than 0. /usr/src/sys/ufs/ufs/ufs_vnops.c: int ufs.setattr(ap) ... { if ((cred->cr.uid == 0) && (p->prison == NULL)) { if ((ip->i_flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) && securelevel > 0) return (EPERM); }
diff --git a/en_US.ISO8859-1/books/developers-handbook/mac/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/mac/chapter.sgml index ccd16eaf70..1946e991c0 100644 --- a/en_US.ISO8859-1/books/developers-handbook/mac/chapter.sgml +++ b/en_US.ISO8859-1/books/developers-handbook/mac/chapter.sgml @@ -1,7819 +1,7821 @@ Chris Costello TrustedBSD Project
chris@FreeBSD.org
Robert Watson TrustedBSD Project
rwatson@FreeBSD.org
The TrustedBSD MAC Framework MAC Documentation Copyright This documentation was developed for the FreeBSD Project by Chris Costello at Safeport Network Services and Network Associates Laboratories, the Security Research Division of Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 (CBOSS), as part of the DARPA CHATS research program. Redistribution and use in source (SGML DocBook) and 'compiled' forms (SGML, HTML, PDF, PostScript, RTF and so forth) with or without modification, are permitted provided that the following conditions are met: Redistributions of source code (SGML DocBook) must retain the above copyright notice, this list of conditions and the following disclaimer as the first lines of this file unmodified. Redistributions in compiled form (transformed to other DTDs, converted to PDF, PostScript, RTF and other formats) 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 DOCUMENTATION IS PROVIDED BY THE NETWORKS ASSOCIATES TECHNOLOGY, INC "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 NETWORKS ASSOCIATES TECHNOLOGY, INC 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 DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Synopsis FreeBSD includes experimental support for several mandatory access control policies, as well as a framework for kernel security extensibility, the TrustedBSD MAC Framework. The MAC Framework provides a pluggable access control framework, permitting new security policies to be easily linked into the kernel, loaded at boot, or loaded dynamically at run-time. The framework provides a variety of features to make it easier to implement new policies, including the ability to easily tag security labels (such as confidentiality information) onto system objects. This chapter introduces the MAC policy framework and provides documentation for a sample MAC policy module. Introduction The TrustedBSD MAC framework provides a mechanism to allow the compile-time or run-time extension of the kernel access control model. New system policies may be implemented as kernel modules and linked to the kernel; if multiple policy modules are present, their results will be composed. The MAC Framework provides a variety of access control infrastructure services to assist policy writers, including support for transient and persistent policy-agnostic object security labels. This support is currently considered experimental. Policy Background Mandatory Access Control (MAC), refers to a set of access control policies that are mandatorily enforced on users by the operating system. MAC policies may be contrasted with Discretionary Access Control (DAC) protections, by which non-administrative users may (at their discretion) protect objects. In traditional UNIX systems, DAC protections include file permissions and access control lists; MAC protections include process controls preventing inter-user debugging and firewalls. A variety of MAC policies have been formulated by operating system designers and security researches, including the Multi-Level Security (MLS) confidentiality policy, the Biba integrity policy, Role-Based Access Control (RBAC), and Type Enforcement (TE). Each model bases decisions on a variety of factors, including user identity, role, and security clearance, as well as security labels on objects representing concepts such as data sensitivity and integrity. The TrustedBSD MAC Framework is capable of supporting policy modules that implement all of these policies, as well as a broad class of system hardening policies. In addition, despite the name, the MAC Framework can also be used to implement purely discretionary policies, as policy modules are given substantial flexibility in how they authorize protections. MAC Framework Kernel Architecture The TrustedBSD MAC Framework permits kernel modules to extend the operating system security policy, as well as providing infrastructure functionality required by many access control modules. If multiple policies are simultaneously loaded, the MAC Framework will usefully (for some definition of useful) compose the results of the policies. Kernel Elements The MAC Framework contains a number of kernel elements: Framework management interfaces Concurrency and synchronization primitives. Policy registration Extensible security label for kernel objects Policy entry point composition operators Label management primitives Entry point API invoked by kernel services Entry point API to policy modules Entry points implementations (policy life cycle, object life cycle/label management, access control checks). Policy-agnostic label-management system calls mac_syscall() multiplex system call Various security policies implemented as MAC policy modules Management Interfaces The TrustedBSD MAC Framework may be directly managed using sysctls, loader tunables, and system calls. In most cases, sysctls and loader tunables modify the same parameters, and control behavior such as enforcement of protections relating to various kernel subsystems. In addition, if MAC debugging support is compiled into the kernel, a variety of counters will be maintained tracking label allocation. In most cases, it is advised that per-subsystem enforcement controls not be used to control policy behavior in production environments, as they broadly impact the operation of all active policies. Instead, per-policy controls should be preferred to ensure proper policy operation. Loading and unloading of policy modules is performed using the system module management system calls and other system interfaces, including loader variables. Concurrency and Synchronization As the set of active policies may change at run-time, and the invocation of entry points is non-atomic, synchronization is required to prevent unloading or loading of new policies while an entry point invocation is progress, freezing the list of policies for the duration. This is accomplished by means of a Framework busy count. Whenever an entry point is entered, the busy count is incremented; whenever it is exited, the busy count is decremented. While the busy count is elevated, policy list changes are not permitted, and threads attempting to modify the policy list will sleep until the list is not busy. The busy count is protected by a mutex, and a condition variable is used to wake up sleepers waiting on policy list modifications. Various optimizations are used to reduce the overhead of the busy count, including avoiding the full cost of incrementing and decrementing if the list is empty or contains only static entries (policies that are loaded before the system starts, and cannot be unloaded). Policy Registration The MAC Framework maintains two lists of active policies: a static list, and a dynamic list. The lists differ only with regards to their locking semantics: an elevated reference count is not required to make use of the static list. When kernel modules containing MAC Framework policies are loaded, the policy module will use SYSINIT to invoke a registration function; when a policy module is unloaded, SYSINIT will likewise invoke a de-registration function. Registration may fail if a policy module is loaded more than once, if insufficient resources are available for the registration (for example, the policy might require labeling and insufficient labeling state might be available), or other policy prerequisites might not be met (some policies may only be loaded prior to boot). Likewise, de-registration may fail if a policy refuses an unload. Entry Points Kernel services interact with the MAC Framework in two ways: they invoke a series of APIs to notify the framework of relevant events, and they a policy-agnostic label structure in security-relevant objects. This label structure is maintained by the MAC Framework via label management entry points, and permits the Framework to offer a labeling service to policy modules through relatively non-invasive changes to the kernel subsystem maintaining the object. For example, label structures have been added to processes, process credentials, sockets, pipes, vnodes, Mbufs, network interfaces, IP reassembly queues, and a variety of other security-relevant structures. Kernel services also invoke the MAC Framework when they perform important security decisions, permitting policy modules to augment those decisions based on their own criteria (possibly including data stored in security labels). Policy Composition When more than one policy module is loaded into the kernel at a time, the results of the policy modules will be composed by the framework using a composition operator. This operator is currently hard-coded, and requires that all active policies must approve a request for it to occur. As policies may return a variety of error conditions (success, access denied, object doesn't exist, ...), a precedence operator selects the resulting error from the set of errors returned by policies. While it is not guaranteed that the resulting composition will be useful or secure, we've found that it is for many useful selections of policies. Labeling Support As many interesting access control extensions rely on security labels on objects, the MAC Framework provides a set of policy-agnostic label management system calls covering a variety of user-exposed objects. Common label types include partition identifiers, sensitivity labels, integrity labels, compartments, domains, roles, and types. Policy modules participate in the internalization and externalization of string-based labels provides by user applications, and can expose multiple label elements to applications if desired. In-memory labels are stored in struct label, which consists of a fixed-length array of unions, each holding a void * pointer and a long. Policies registering for label storage will be assigned a "slot" identifier, which may be used to dereference the label storage. The semantics of the storage are left entirely up to the policy module: modules are provided with a variety of entry points associated with the kernel object life cycle, including initialization, association/creation, and destruction. Using these interfaces, it is possible to implement reference counting and other storage mechanisms. Direct access to the kernel object is generally not required by policy modules to retrieve a label, as the MAC Framework generally passes both a pointer to the object and a direct pointer to the object's label into entry points. Initialization entry points frequently include a blocking disposition flag indicating whether or not an initialization is permitted to block; if blocking is not permitted, a failure may be returned to cancel allocation of the label. This may occur, for example, in the network stack during interrupt handling, where blocking is not permitted. Due to the performance cost of maintaining labels on in-flight network packets (Mbufs), policies must specifically declare a requirement that Mbuf labels be allocated. Dynamically loaded policies making use of labels must be able to handle the case where their init function has not been called on an object, as objects may already exist when the policy is loaded. In the case of file system labels, special support is provided for the persistent storage of security labels in extended attributes. Where available, EA transactions are used to permit consistent compound updates of security labels on vnodes. Currently, if a labeled policy permits dynamic unloading, its state slot cannot be reclaimed. System Calls The MAC Framework implements a number of system calls: most of these calls support the policy-agnostic label retrieval and manipulation APIs exposed to user applications. The label management calls accept a label description structure, struct mac, which contains a series of MAC label elements. Each element contains a character string name, and character string value. Each policy will be given the chance to claim a particular element name, permitting policies to expose multiple independent elements if desired. Policy modules perform the internalization and externalization between kernel labels and user-provided labels via entry points, permitting a variety of semantics. Label management system calls are generally wrapped by user library functions to perform memory allocation and error handling. In addition, mac_syscall() permits policy modules to create new system calls without allocating system calls. mac_execve() permits an atomic process credential label change when executing a new image. MAC Policy Architecture Security policies are either linked directly into the kernel, or compiled into loadable kernel modules that may be loaded at boot, or dynamically using the module loading system calls at runtime. Policy modules interact with the system through a set of declared entry points, providing access to a stream of system events and permitting the policy to influence access control decisions. Each policy contains a number of elements: Optional configuration parameters for policy. Centralized implementation of the policy logic and parameters. Optional implementation of policy life cycle events, such as initialization and destruction. Optional support for initializing, maintaining, and destroying labels on selected kernel objects. Optional support for user process inspection and modification of labels on selected objects. Implementation of selected access control entry points that are of interest to the policy. Declaration of policy identity, module entry points, and policy properties. Policy Declaration Modules may be declared using the MAC_POLICY_SET() macro, which names the policy, provides a reference to the MAC entry point vector, provides load-time flags determining how the policy framework should handle the policy, and optionally requests the allocation of label state by the framework. static struct mac_policy_ops mac_policy_ops = { .mpo_destroy = mac_policy_destroy, .mpo_init = mac_policy_init, .mpo_init_bpfdesc_label = mac_policy_init_bpfdesc_label, .mpo_init_cred_label = mac_policy_init_label, /* ... */ .mpo_check_vnode_setutimes = mac_policy_check_vnode_setutimes, .mpo_check_vnode_stat = mac_policy_check_vnode_stat, .mpo_check_vnode_write = mac_policy_check_vnode_write, }; The MAC policy entry point vector, mac_policy_ops in this example, associates functions defined in the module with specific entry points. A complete listing of available entry points and their prototypes may be found in the MAC entry point reference section. Of specific interest during module registration are the .mpo_destroy and .mpo_init entry points. .mpo_init will be invoked once a policy is successfully registered with the module framework but prior to any other entry points becoming active. This permits the policy to perform any policy-specific allocation and initialization, such as initialization of any data or locks. .mpo_destroy will be invoked when a policy module is unloaded to permit releasing of any allocated memory and destruction of locks. Currently, these two entry points are invoked with the MAC policy list mutex held to prevent any other entry points from being invoked: this will be changed, but in the mean time, policies should be careful about what kernel primitives they invoke so as to avoid lock ordering or sleeping problems. The policy declaration's module name field exists so that the module may be uniquely identified for the purposes of module dependencies. An appropriate string should be selected. The full string name of the policy is displayed to the user via the kernel log during load and unload events, and also exported when providing status information to userland processes. Policy Flags The policy declaration flags field permits the module to provide the framework with information about its capabilities at the time the module is loaded. Currently, three flags are defined: MPC_LOADTIME_FLAG_UNLOADOK This flag indicates that the policy module may be unloaded. If this flag is not provided, then the policy framework will reject requests to unload the module. This flag might be used by modules that allocate label state and are unable to free that state at runtime. MPC_LOADTIME_FLAG_NOTLATE This flag indicates that the policy module must be loaded and initialized early in the boot process. If the flag is specified, attempts to register the module following boot will be rejected. The flag may be used by policies that require pervasive labeling of all system objects, and cannot handle objects that have not been properly initialized by the policy. MPC_LOADTIME_FLAG_LABELMBUFS This flag indicates that the policy module requires labeling of Mbufs, and that memory should always be allocated for the storage of Mbuf labels. By default, the MAC Framework will not allocate label storage for Mbufs unless at least one loaded policy has this flag set. This measurably improves network performance when policies do not require Mbuf labeling. A kernel option, MAC_ALWAYS_LABEL_MBUF, exists to force the MAC Framework to allocate Mbuf label storage regardless of the setting of this flag, and may be useful in some environments. Policies using the MPC_LOADTIME_FLAG_LABELMBUFS without the MPC_LOADTIME_FLAG_NOTLATE flag set must be able to correctly handle NULL Mbuf label pointers passed into entry points. This is necessary as in-flight Mbufs without label storage may persist after a policy enabling Mbuf labeling has been loaded. If a policy is loaded before the network subsystem is active (i.e., the policy is not being loaded late), then all Mbufs are guaranteed to have label storage. Policy Entry Points Four classes of entry points are offered to policies registered with the framework: entry points associated with the registration and management of policies, entry points denoting initialization, creation, destruction, and other life cycle events for kernel objects, events associated with access control decisions that the policy module may influence, and calls associated with the management of labels on objects. In addition, a mac_syscall() entry point is provided so that policies may extend the kernel interface without registering new system calls. Policy module writers should be aware of the kernel locking strategy, as well as what object locks are available during which entry points. Writers should attempt to avoid deadlock scenarios by avoiding grabbing non-leaf locks inside of entry points, and also follow the locking protocol for object access and modification. In particular, writers should be aware that while necessary locks to access objects and their labels are generally held, sufficient locks to modify an object or its label may not be present for all entry points. Locking information for arguments is documented in the MAC framework entry point document. Policy entry points will pass a reference to the object label along with the object itself. This permits labeled policies to be unaware of the internals of the object yet still make decisions based on the label. The exception to this is the process credential, which is assumed to be understood by policies as a first class security object in the kernel. Policies that do not implement labels on kernel objects will be passed NULL pointers for label arguments to entry points. MAC Policy Entry Point Reference General-Purpose Module Entry Points <function>&mac.mpo;_init</function void &mac.mpo;_init struct mac_policy_conf *conf &mac.thead; conf MAC policy definition Policy load event. The policy list mutex is held, so caution should be applied. <function>&mac.mpo;_destroy</function> void &mac.mpo;_destroy struct mac_policy_conf *conf &mac.thead; conf MAC policy definition Policy load event. The policy list mutex is held, so caution should be applied. <function>&mac.mpo;_syscall</function> int &mac.mpo;_syscall struct thread *td int call void *arg &mac.thead; td Calling thread call Syscall number arg Pointer to syscall arguments This entry point provides a policy-multiplexed system call so that policies may provide additional services to user processes without registering specific system calls. The policy name provided during registration is used to demux calls from userland, and the arguments will be forwarded to this entry point. When implementing new services, security modules should be sure to invoke appropriate access control checks from the MAC framework as needed. For example, if a policy implements an augmented signal functionality, it should call the necessary signal access control checks to invoke the MAC framework and other registered policies. Modules must currently perform the copyin() of the syscall data on their own. <function>&mac.mpo;_thread_userret</function> void &mac.mpo;_thread_userret struct thread *td &mac.thead; td Returning thread This entry point permits policy modules to perform MAC-related events when a thread returns to user space. This is required for policies that have floating process labels, as it's not always possible to acquire the process lock at arbitrary points in the stack during system call processing; process labels might represent traditional authentication data, process history information, or other data. Label Operations <function>&mac.mpo;_init_bpfdesc_label</function> void &mac.mpo;_init_bpfdesc_label struct label *label &mac.thead; label New label to apply Initialize the label on a newly instantiated bpfdesc (BPF descriptor) <function>&mac.mpo;_init_cred_label</function> void &mac.mpo;_init_cred_label struct label *label &mac.thead; label New label to initialize Initialize the label for a newly instantiated user credential. <function>&mac.mpo;_init_devfsdirent_label</function> void &mac.mpo;_init_devfsdirent_label struct label *label &mac.thead; label New label to apply Initialize the label on a newly instantiated devfs entry. <function>&mac.mpo;_init_ifnet_label</function> void &mac.mpo;_init_ifnet_label struct label *label &mac.thead; label New label to apply Initialize the label on a newly instantiated network interface. <function>&mac.mpo;_init_ipq_label</function> void &mac.mpo;_init_ipq_label struct label *label int flag &mac.thead; label New label to apply flag Blocking/non-blocking &man.malloc.9;; see below Initialize the label on a newly instantiated IP fragment reassembly queue. The flag field may be one of M_WAITOK and M_NOWAIT, and should be employed to avoid performing a blocking &man.malloc.9; during this initialization call. IP fragment reassembly queue allocation frequently occurs in performance sensitive environments, and the implementation should be careful to avoid blocking or long-lived operations. This entry point is permitted to fail resulting in the failure to allocate the IP fragment reassembly queue. <function>&mac.mpo;_init_mbuf_label</function> void &mac.mpo;_init_mbuf_label int flag struct label *label &mac.thead; flag Blocking/non-blocking &man.malloc.9;; see below label Policy label to initialize + Initialize the label on a newly instantiated mbuf packet header (mbuf). The flag field may be one of M_WAITOK and M_NOWAIT, and should be employed to avoid performing a blocking &man.malloc.9; during this initialization call. Mbuf allocation frequently occurs in performance sensitive environments, and the implementation should be careful to avoid blocking or long-lived operations. This entry point is permitted to fail resulting in the failure to allocate the mbuf header. <function>&mac.mpo;_init_mount_label</function> void &mac.mpo;_init_mount_label struct label *mntlabel struct label *fslabel &mac.thead; mntlabel Policy label to be initialized for the mount itself fslabel Policy label to be initialized for the file system Initialize the labels on a newly instantiated mount point. <function>&mac.mpo;_init_mount_fs_label</function> void &mac.mpo;_init_mount_fs_label struct label *label &mac.thead; label Label to be initialized Initialize the label on a newly mounted file system. <function>&mac.mpo;_init_pipe_label</function> void &mac.mpo;_init_pipe_label struct label*label &mac.thead; label Label to be filled in + Initialize a label for a newly instantiated pipe. <function>&mac.mpo;_init_socket_label</function> void &mac.mpo;_init_socket_label struct label *label int flag &mac.thead; label New label to initialize flag &man.malloc.9; flags Initialize a label for a newly instantiated socket. <function>&mac.mpo;_init_socket_peer_label</function> void &mac.mpo;_init_socket_peer_label struct label *label int flag &mac.thead; label New label to initialize flag &man.malloc.9; flags Initialize the peer label for a newly instantiated socket. <function>&mac.mpo;_init_proc_label</function> void &mac.mpo;_init_proc_label struct label *label &mac.thead; label New label to initialize Initialize the label for a newly instantiated process. <function>&mac.mpo;_init_vnode_label</function> void &mac.mpo;_init_vnode_label struct label *label &mac.thead; label New label to initialize Initialize the label on a newly instantiated vnode. <function>&mac.mpo;_destroy_bpfdesc_label</function> void &mac.mpo;_destroy_bpfdesc_label struct label *label &mac.thead; label bpfdesc label Destroy the label on a BPF descriptor. In this entry point a policy should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_cred_label</function> void &mac.mpo;_destroy_cred_label struct label *label &mac.thead; label Label being destroyed Destroy the label on a credential. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_devfsdirent_label</function> void &mac.mpo;_destroy_devfsdirent_label struct label *label &mac.thead; label Label being destroyed Destroy the label on a devfs entry. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_ifnet_label</function> void &mac.mpo;_destroy_ifnet_label struct label *label &mac.thead; label Label being destroyed Destroy the label on a removed interface. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_ipq_label</function> void &mac.mpo;_destroy_ipq_label struct label *label &mac.thead; label Label being destroyed Destroy the label on an IP fragment queue. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_mbuf_label</function> void &mac.mpo;_destroy_mbuf_label struct label *label &mac.thead; label Label being destroyed Destroy the label on an mbuf header. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_mount_label</function> void &mac.mpo;_destroy_mount_label struct label *label &mac.thead; label Mount point label being destroyed Destroy the labels on a mount point. In this entry point, a policy module should free the internal storage associated with mntlabel so that they may be destroyed. <function>&mac.mpo;_destroy_mount_label</function> void &mac.mpo;_destroy_mount_label struct label *mntlabel struct label *fslabel &mac.thead; mntlabel Mount point label being destroyed fslabel - File system label being destroyed> + File system label being destroyed> Destroy the labels on a mount point. In this entry point, a policy module should free the internal storage associated with mntlabel and fslabel so that they may be destroyed. <function>&mac.mpo;_destroy_socket_label</function> void &mac.mpo;_destroy_socket_label struct label *label &mac.thead; label Socket label being destroyed Destroy the label on a socket. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_socket_peer_label</function> void &mac.mpo;_destroy_socket_peer_label struct label *peerlabel &mac.thead; peerlabel Socket peer label being destroyed Destroy the peer label on a socket. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_pipe_label</function> void &mac.mpo;_destroy_pipe_label struct label *label &mac.thead; label Pipe label Destroy the label on a pipe. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_proc_label</function> void &mac.mpo;_destroy_proc_label struct label *label &mac.thead; label Process label Destroy the label on a process. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_destroy_vnode_label</function> void &mac.mpo;_destroy_vnode_label struct label *label &mac.thead; label Process label Destroy the label on a vnode. In this entry point, a policy module should free any internal storage associated with label so that it may be destroyed. <function>&mac.mpo;_copy_mbuf_label</function> void &mac.mpo;_copy_mbuf_label struct label *src struct label *dest &mac.thead; src Source label dest Destination label Copy the label information in src into dest. <function>&mac.mpo;_copy_pipe_label</function> void &mac.mpo;_copy_pipe_label struct label *src struct label *dest &mac.thead; src Source label dest Destination label Copy the label information in src into dest. <function>&mac.mpo;_copy_vnode_label</function> void &mac.mpo;_copy_vnode_label struct label *src struct label *dest &mac.thead; src Source label dest Destination label Copy the label information in src into dest. <function>&mac.mpo;_externalize_cred_label</function> int &mac.mpo;_externalize_cred_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_ifnet_label</function> int &mac.mpo;_externalize_ifnet_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_pipe_label</function> int &mac.mpo;_externalize_pipe_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_socket_label</function> int &mac.mpo;_externalize_socket_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_socket_peer_label</function> int &mac.mpo;_externalize_socket_peer_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_externalize_vnode_label</function> int &mac.mpo;_externalize_vnode_label &mac.externalize.paramdefs; &mac.thead; &mac.externalize.tbody; &mac.externalize.para; <function>&mac.mpo;_internalize_cred_label</function> int &mac.mpo;_internalize_cred_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_ifnet_label</function> int &mac.mpo;_internalize_ifnet_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_pipe_label</function> int &mac.mpo;_internalize_pipe_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_socket_label</function> int &mac.mpo;_internalize_socket_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; <function>&mac.mpo;_internalize_vnode_label</function> int &mac.mpo;_internalize_vnode_label &mac.internalize.paramdefs; &mac.thead; &mac.internalize.tbody; &mac.internalize.para; Label Events This class of entry points is used by the MAC framework to permit policies to maintain label information on kernel objects. For each labeled kernel object of interest to a MAC policy, entry points may be registered for relevant life cycle events. All objects implement initialization, creation, and destruction hooks. Some objects will also implement relabeling, allowing user processes to change the labels on objects. Some objects will also implement object-specific events, such as label events associated with IP reassembly. A typical labeled object will have the following life cycle of entry points: Label initialization o (object-specific wait) \ Label creation o \ Relabel events, o--<--. Various object-specific, | | Access control events ~-->--o \ Label destruction o Label initialization permits policies to allocate memory and set initial values for labels without context for the use of the object. The label slot allocated to a policy will be zeroed by default, so some policies may not need to perform initialization. Label creation occurs when the kernel structure is associated with an actual kernel object. For example, Mbufs may be allocated and remain unused in a pool until they are required. mbuf allocation causes label initialization on the mbuf to take place, but mbuf creation occurs when the mbuf is associated with a datagram. Typically, context will be provided for a creation event, including the circumstances of the creation, and labels of other relevant objects in the creation process. For example, when an mbuf is created from a socket, the socket and its label will be presented to registered policies in addition to the new mbuf and its label. Memory allocation in creation events is discouraged, as it may occur in performance sensitive ports of the kernel; in addition, creation calls are not permitted to fail so a failure to allocate memory cannot be reported. Object specific events do not generally fall into the other broad classes of label events, but will generally provide an opportunity to modify or update the label on an object based on additional context. For example, the label on an IP fragment reassembly queue may be updated during the MAC_UPDATE_IPQ entry point as a result of the acceptance of an additional mbuf to that queue. Access control events are discussed in detail in the following section. Label destruction permits policies to release storage or state associated with a label during its association with an object so that the kernel data structures supporting the object may be reused or released. In addition to labels associated with specific kernel objects, an additional class of labels exists: temporary labels. These labels are used to store update information submitted by user processes. These labels are initialized and destroyed as with other label types, but the creation event is MAC_INTERNALIZE, which accepts a user label to be converted to an in-kernel representation. File System Object Labeling Event Operations <function>&mac.mpo;_associate_vnode_devfs</function> void &mac.mpo;_associate_vnode_devfs struct mount *mp struct label *fslabel struct devfs_dirent *de struct label *delabel struct vnode *vp struct label *vlabel &mac.thead; mp Devfs mount point fslabel Devfs file system label (mp->mnt_fslabel) de Devfs directory entry delabel Policy label associated with de vp vnode associated with de vlabel Policy label associated with vp Fill in the label (vlabel) for a newly created devfs vnode based on the devfs directory entry passed in de and its label. <function>&mac.mpo;_associate_vnode_extattr</function> int &mac.mpo;_associate_vnode_extattr struct mount *mp struct label *fslabel struct vnode *vp struct label *vlabel &mac.thead; mp File system mount point fslabel File system label vp Vnode to label vlabel Policy label associated with vp Attempt to retrieve the label for vp from the file system extended attributes. Upon success, the value 0 is returned. Should extended attribute retrieval not be supported, an accepted fallback is to copy fslabel into vlabel. In the event of an error, an appropriate value for errno should be returned. <function>&mac.mpo;_associate_vnode_singlelabel</function> void &mac.mpo;_associate_vnode_singlelabel struct mount *mp struct label *fslabel struct vnode *vp struct label *vlabel &mac.thead; mp File system mount point fslabel File system label vp Vnode to label vlabel Policy label associated with vp On non-multilabel file systems, this entry point is called to set the policy label for vp based on the file system label, fslabel. <function>&mac.mpo;_create_devfs_device</function> void &mac.mpo;_create_devfs_device dev_t dev struct devfs_dirent *devfs_dirent struct label *label &mac.thead; dev Device corresponding with devfs_dirent devfs_dirent Devfs directory entry to be labeled. label Label for devfs_dirent to be filled in. Fill out the label on a devfs_dirent being created for the passed device. This call will be made when the device file system is mounted, regenerated, or a new device is made available. <function>&mac.mpo;_create_devfs_directory</function> void &mac.mpo;_create_devfs_directory char *dirname int dirnamelen struct devfs_dirent *devfs_dirent struct label *label &mac.thead; dirname Name of directory being created namelen Length of string dirname devfs_dirent Devfs directory entry for directory being created. Fill out the label on a devfs_dirent being created for the passed directory. This call will be made when the device file system is mounted, regenerated, or a new device requiring a specific directory hierarchy is made available. <function>&mac.mpo;_create_devfs_symlink</function> void &mac.mpo;_create_devfs_symlink struct ucred *cred struct mount *mp struct devfs_dirent *dd struct label *ddlabel struct devfs_dirent *de struct label *delabel &mac.thead; cred Subject credential mp Devfs mount point dd Link destination ddlabel Label associated with dd de Symlink entry delabel Label associated with de Fill in the label (delabel) for a newly created &man.devfs.5; symbolic link entry. <function>&mac.mpo;_create_vnode_extattr</function> int &mac.mpo;_create_vnode_extattr struct ucred *cred struct mount *mp struct label *fslabel struct vnode *dvp struct label *dlabel struct vnode *vp struct label *vlabel struct componentname *cnp &mac.thead; cred Subject credential mount File system mount point label File system label dvp Parent directory vnode dlabel Label associated with dvp vp Newly created vnode vlabel Policy label associated with vp cnp Component name for vp Write out the label for vp to the appropriate extended attribute. If the write succeeds, fill in vlabel with the label, and return 0. Otherwise, return an appropriate error. <function>&mac.mpo;_create_mount</function> void &mac.mpo;_create_mount struct ucred *cred struct mount *mp struct label *mnt struct label *fslabel &mac.thead; cred Subject credential mp Object; file system being mounted mntlabel Policy label to be filled in for mp fslabel Policy label for the file system mp mounts. Fill out the labels on the mount point being created by the passed subject credential. This call will be made when a new file system is mounted. <function>&mac.mpo;_create_root_mount</function> void &mac.mpo;_create_root_mount struct ucred *cred struct mount *mp struct label *mntlabel struct label *fslabel &mac.thead; See . Fill out the labels on the mount point being created by the passed subject credential. This call will be made when the root file system is mounted, after &mac.mpo;_create_mount;. <function>&mac.mpo;_relabel_vnode</function> void &mac.mpo;_relabel_vnode struct ucred *cred struct vnode *vp struct label *vnodelabel struct label *newlabel &mac.thead; cred Subject credential vp vnode to relabel vnodelabel Existing policy label for vp newlabel New, possibly partial label to replace vnodelabel Update the label on the passed vnode given the passed update vnode label and the passed subject credential. <function>&mac.mpo;_setlabel_vnode_extattr</function> int &mac.mpo;_setlabel_vnode_extattr struct ucred *cred struct vnode *vp struct label *vlabel struct label *intlabel &mac.thead; cred Subject credential vp Vnode for which the label is being written vlabel Policy label associated with vp intlabel Label to write out Write out the policy from intlabel to an extended attribute. This is called from vop_stdcreatevnode_ea. <function>&mac.mpo;_update_devfsdirent</function> void &mac.mpo;_update_devfsdirent struct devfs_dirent *devfs_dirent struct label *direntlabel struct vnode *vp struct label *vnodelabel &mac.thead; devfs_dirent Object; devfs directory entry direntlabel Policy label for devfs_dirent to be updated. vp Parent vnode Locked vnodelabel Policy label for vp Update the devfs_dirent label from the passed devfs vnode label. This call will be made when a devfs vnode has been successfully relabeled to commit the label change such that it lasts even if the vnode is recycled. It will also be made when when a symlink is created in devfs, following a call to mac_vnode_create_from_vnode to initialize the vnode label. IPC Object Labeling Event Operations <function>&mac.mpo;_create_mbuf_from_socket</function> void &mac.mpo;_create_mbuf_from_socket struct socket *so struct label *socketlabel struct mbuf *m struct label *mbuflabel &mac.thead; socket Socket Socket locking WIP socketlabel Policy label for socket m Object; mbuf mbuflabel Policy label to fill in for m Set the label on a newly created mbuf header from the passed socket label. This call is made when a new datagram or message is generated by the socket and stored in the passed mbuf. <function>&mac.mpo;_create_pipe</function> void &mac.mpo;_create_pipe struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Set the label on a newly created pipe from the passed subject credential. This call is made when a new pipe is created. <function>&mac.mpo;_create_socket</function> void &mac.mpo;_create_socket struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential Immutable so Object; socket to label socketlabel Label to fill in for so Set the label on a newly created socket from the passed subject credential. This call is made when a socket is created. <function>&mac.mpo;_create_socket_from_socket</function> void &mac.mpo;_create_socket_from_socket struct socket *oldsocket struct label *oldsocketlabel struct socket *newsocket struct label *newsocketlabel &mac.thead; oldsocket Listening socket oldsocketlabel Policy label associated with oldsocket newsocket New socket newsocketlabel Policy label associated with newsocketlabel Label a socket, newsocket, newly &man.accept.2;ed, based on the &man.listen.2; socket, oldsocket. <function>&mac.mpo;_relabel_pipe</function> void &mac.mpo;_relabel_pipe struct ucred *cred struct pipe *pipe struct label *oldlabel struct label *newlabel &mac.thead; cred Subject credential pipe Pipe oldlabel Current policy label associated with pipe newlabel Policy label update to apply to pipe Apply a new label, newlabel, to pipe. <function>&mac.mpo;_relabel_socket</function> void &mac.mpo;_relabel_socket struct ucred *cred struct socket *so struct label *oldlabel struct label *newlabel &mac.thead; cred Subject credential Immutable so Object; socket oldlabel Current label for so newlabel Label update for so Update the label on a socket from the passed socket label update. <function>&mac.mpo;_set_socket_peer_from_mbuf</function> void &mac.mpo;_set_socket_peer_from_mbuf struct mbuf *mbuf struct label *mbuflabel struct label *oldlabel struct label *newlabel &mac.thead; mbuf First datagram received over socket mbuflabel Label for mbuf oldlabel Current label for the socket newlabel Policy label to be filled out for the socket Set the peer label on a stream socket from the passed mbuf label. This call will be made when the first datagram is received by the stream socket, with the exception of Unix domain sockets. <function>&mac.mpo;_set_socket_peer_from_socket</function> void &mac.mpo;_set_socket_peer_from_socket struct socket *oldsocket struct label *oldsocketlabel struct socket *newsocket struct label *newsocketpeerlabel &mac.thead; oldsocket Local socket oldsocketlabel Policy label for oldsocket newsocket Peer socket newsocketpeerlabel Policy label to fill in for newsocket Set the peer label on a stream UNIX domain socket from the passed remote socket endpoint. This call will be made when the socket pair is connected, and will be made for both endpoints. Network Object Labeling Event Operations <function>&mac.mpo;_create_bpfdesc</function> void &mac.mpo;_create_bpfdesc struct ucred *cred struct bpf_d *bpf_d struct label *bpflabel &mac.thead; cred Subject credential Immutable bpf_d Object; bpf descriptor bpf Policy label to be filled in for bpf_d Set the label on a newly created BPF descriptor from the passed subject credential. This call will be made when a BPF device node is opened by a process with the passed subject credential. <function>&mac.mpo;_create_ifnet</function> void &mac.mpo;_create_ifnet struct ifnet *ifnet struct label *ifnetlabel &mac.thead; ifnet Network interface ifnetlabel Policy label to fill in for ifnet Set the label on a newly created interface. This call may be made when a new physical interface becomes available to the system, or when a pseudo-interface is instantiated during the boot or as a result of a user action. <function>&mac.mpo;_create_ipq</function> void &mac.mpo;_create_ipq struct mbuf *fragment struct label *fragmentlabel struct ipq *ipq struct label *ipqlabel &mac.thead; fragment First received IP fragment fragmentlabel Policy label for fragment ipq IP reassembly queue to be labeled ipqlabel Policy label to be filled in for ipq Set the label on a newly created IP fragment reassembly queue from the mbuf header of the first received fragment. <function>&mac.mpo;_create_datagram_from_ipq</function> void &mac.mpo;_create_create_datagram_from_ipq struct ipq *ipq struct label *ipqlabel struct mbuf *datagram struct label *datagramlabel &mac.thead; ipq IP reassembly queue ipqlabel Policy label for ipq datagram Datagram to be labeled datagramlabel Policy label to be filled in for datagramlabel Set the label on a newly reassembled IP datagram from the IP fragment reassembly queue from which it was generated. <function>&mac.mpo;_create_fragment</function> void &mac.mpo;_create_fragment struct mbuf *datagram struct label *datagramlabel struct mbuf *fragment struct label *fragmentlabel &mac.thead; datagram Datagram datagramlabel Policy label for datagram fragment Fragment to be labeled fragmentlabel Policy label to be filled in for datagram Set the label on the mbuf header of a newly created IP fragment from the label on the mbuf header of the datagram it was generate from. <function>&mac.mpo;_create_mbuf_from_mbuf</function> void &mac.mpo;_create_mbuf_from_mbuf struct mbuf *oldmbuf struct label *oldmbuflabel struct mbuf *newmbuf struct label *newmbuflabel &mac.thead; oldmbuf Existing (source) mbuf oldmbuflabel Policy label for oldmbuf newmbuf New mbuf to be labeled newmbuflabel Policy label to be filled in for newmbuf Set the label on the mbuf header of a newly created datagram from the mbuf header of an existing datagram. This call may be made in a number of situations, including when an mbuf is re-allocated for alignment purposes. <function>&mac.mpo;_create_mbuf_linklayer</function> void &mac.mpo;_create_mbuf_linklayer struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; ifnet Network interface ifnetlabel Policy label for ifnet mbuf mbuf header for new datagram mbuflabel Policy label to be filled in for mbuf Set the label on the mbuf header of a newly created datagram generated for the purposes of a link layer response for the passed interface. This call may be made in a number of situations, including for ARP or ND6 responses in the IPv4 and IPv6 stacks. <function>&mac.mpo;_create_mbuf_from_bpfdesc</function> void &mac.mpo;_create_mbuf_from_bpfdesc struct bpf_d *bpf_d struct label *bpflabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; bpf_d BPF descriptor bpflabel Policy label for bpflabel mbuf New mbuf to be labeled mbuflabel Policy label to fill in for mbuf Set the label on the mbuf header of a newly created datagram generated using the passed BPF descriptor. This call is made when a write is performed to the BPF device associated with the passed BPF descriptor. <function>&mac.mpo;_create_mbuf_from_ifnet</function> void &mac.mpo;_create_mbuf_from_ifnet struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; ifnet Network interface ifnetlabel Policy label for ifnetlabel mbuf mbuf header for new datagram mbuflabel Policy label to be filled in for mbuf Set the label on the mbuf header of a newly created datagram generated from the passed network interface. <function>&mac.mpo;_create_mbuf_multicast_encap</function> void &mac.mpo;_create_mbuf_multicast_encap struct mbuf *oldmbuf struct label *oldmbuflabel struct ifnet *ifnet struct label *ifnetlabel struct mbuf *newmbuf struct label *newmbuflabel &mac.thead; oldmbuf mbuf header for existing datagram oldmbuflabel Policy label for oldmbuf ifnet Network interface ifnetlabel Policy label for ifnet newmbuf mbuf header to be labeled for new datagram newmbuflabel Policy label to be filled in for newmbuf Set the label on the mbuf header of a newly created datagram generated from the existing passed datagram when it is processed by the passed multicast encapsulation interface. This call is made when an mbuf is to be delivered using the virtual interface. <function>&mac.mpo;_create_mbuf_netlayer</function> void &mac.mpo;_create_mbuf_netlayer struct mbuf *oldmbuf struct label *oldmbuflabel struct mbuf *newmbuf struct label *newmbuflabel &mac.thead; oldmbuf Received datagram oldmbuflabel Policy label for oldmbuf newmbuf Newly created datagram newmbuflabel Policy label for newmbuf Set the label on the mbuf header of a newly created datagram generated by the IP stack in response to an existing received datagram (oldmbuf). This call may be made in a number of situations, including when responding to ICMP request datagrams. <function>&mac.mpo;_fragment_match</function> int &mac.mpo;_fragment_match struct mbuf *fragment struct label *fragmentlabel struct ipq *ipq struct label *ipqlabel &mac.thead; fragment IP datagram fragment fragmentlabel Policy label for fragment ipq IP fragment reassembly queue ipqlabel Policy label for ipq Determine whether an mbuf header containing an IP datagram (fragment) fragment matches the label of the passed IP fragment reassembly queue (ipq). Return (1) for a successful match, or (0) for no match. This call is made when the IP stack attempts to find an existing fragment reassembly queue for a newly received fragment; if this fails, a new fragment reassembly queue may be instantiated for the fragment. Policies may use this entry point to prevent the reassembly of otherwise matching IP fragments if policy does not permit them to be reassembled based on the label or other information. <function>&mac.mpo;_relabel_ifnet</function> void &mac.mpo;_relabel_ifnet struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct label *newlabel &mac.thead; cred Subject credential ifnet Object; Network interface ifnetlabel Policy label for ifnet newlabel Label update to apply to ifnet Update the label of network interface, ifnet, based on the passed update label, newlabel, and the passed subject credential, cred. <function>&mac.mpo;_update_ipq</function> void &mac.mpo;_update_ipq struct mbuf *fragment struct label *fragmentlabel struct ipq *ipq struct label *ipqlabel &mac.thead; mbuf IP fragment mbuflabel Policy label for mbuf ipq IP fragment reassembly queue ipqlabel Policy label to be updated for ipq Update the label on an IP fragment reassembly queue (ipq) based on the acceptance of the passed IP fragment mbuf header (mbuf). Process Labeling Event Operations <function>&mac.mpo;_create_cred</function> void &mac.mpo;_create_cred struct ucred *parent_cred struct ucred *child_cred &mac.thead; parent_cred Parent subject credential child_cred Child subject credential Set the label of a newly created subject credential from the passed subject credential. This call will be made when &man.crcopy.9; is invoked on a newly created struct ucred. This call should not be confused with a process forking or creation event. <function>&mac.mpo;_execve_transition</function> void &mac.mpo;_execve_transition struct ucred *old struct ucred *new struct vnode *vp struct label *vnodelabel &mac.thead; old Existing subject credential Immutable new New subject credential to be labeled vp File to execute Locked vnodelabel Policy label for vp Update the label of a newly created subject credential (new) from the passed existing subject credential (old) based on a label transition caused by executing the passed vnode (vp). This call occurs when a process executes the passed vnode and one of the policies returns a success from the mpo_execve_will_transition entry point. Policies may choose to implement this call simply by invoking mpo_create_cred and passing the two subject credentials so as not to implement a transitioning event. Policies should not leave this entry point unimplemented if they implement mpo_create_cred, even if they do not implement mpo_execve_will_transition. <function>&mac.mpo;_execve_will_transition</function> int &mac.mpo;_execve_will_transition struct ucred *old struct vnode *vp struct label *vnodelabel &mac.thead; old Subject credential prior to &man.execve.2; Immutable vp File to execute vnodelabel Policy label for vp Determine whether the policy will want to perform a transition event as a result of the execution of the passed vnode by the passed subject credential. Return 1 if a transition is required, 0 if not. Even if a policy returns 0, it should behave correctly in the presence of an unexpected invocation of mpo_execve_transition, as that call may happen as a result of another policy requesting a transition. <function>&mac.mpo;_create_proc0</function> void &mac.mpo;_create_proc0 struct ucred *cred &mac.thead; cred Subject credential to be filled in Create the subject credential of process 0, the parent of all kernel processes. <function>&mac.mpo;_create_proc1</function> void &mac.mpo;_create_proc1 struct ucred *cred &mac.thead; cred Subject credential to be filled in Create the subject credential of process 1, the parent of all user processes. <function>&mac.mpo;_relabel_cred</function> void &mac.mpo;_relabel_cred struct ucred *cred struct label *newlabel &mac.thead; cred Subject credential newlabel Label update to apply to cred Update the label on a subject credential from the passed update label. Access Control Checks Access control entry points permit policy modules to influence access control decisions made by the kernel. Generally, although not always, arguments to an access control entry point will include one or more authorizing credentials, information (possibly including a label) for any other objects involved in the operation. An access control entry point may return 0 to permit the operation, or an &man.errno.2; error value. The results of invoking the entry point across various registered policy modules will be composed as follows: if all modules permit the operation to succeed, success will be returned. If one or modules returns a failure, a failure will be returned. If more than one module returns a failure, the errno value to return to the user will be selected using the following precedence, implemented by the error_select() function in kern_mac.c: Most precedence EDEADLK EINVAL ESRCH EACCES Least precedence EPERM If none of the error values returned by all modules are listed in the precedence chart then an arbitrarily selected value from the set will be returned. In general, the rules provide precedence to errors in the following order: kernel failures, invalid arguments, object not present, access not permitted, other. <function>&mac.mpo;_check_bpfdesc_receive</function> int &mac.mpo;_check_bpfdesc_receive struct bpf_d *bpf_d struct label *bpflabel struct ifnet *ifnet struct label *ifnetlabel &mac.thead; bpf_d Subject; BPF descriptor bpflabel Policy label for bpf_d ifnet Object; network interface ifnetlabel Policy label for ifnet Determine whether the MAC framework should permit datagrams from the passed interface to be delivered to the buffers of the passed BPF descriptor. Return (0) for success, or an errno value for failure Suggested failure: EACCES for label mismatches, EPERM for lack of privilege. <function>&mac.mpo;_check_kenv_dump</function> int &mac.mpo;_check_kenv_dump struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to retrieve the kernel environment (see &man.kenv.2;). <function>&mac.mpo;_check_kenv_get</function> int &mac.mpo;_check_kenv_get struct ucred *cred char *name &mac.thead; cred Subject credential name Kernel environment variable name Determine whether the subject should be allowed to retrieve the value of the specified kernel environment variable. <function>&mac.mpo;_check_kenv_set</function> int &mac.mpo;_check_kenv_set struct ucred *cred char *name &mac.thead; cred Subject credential name Kernel environment variable name Determine whether the subject should be allowed to set the specified kernel environment variable. <function>&mac.mpo;_check_kenv_unset</function> int &mac.mpo;_check_kenv_unset struct ucred *cred char *name &mac.thead; cred Subject credential name Kernel environment variable name Determine whether the subject should be allowed to unset the specified kernel environment variable. <function>&mac.mpo;_check_kld_load</function> int &mac.mpo;_check_kld_load struct ucred *cred struct vnode *vp struct label *vlabel &mac.thead; cred Subject credential vp Kernel module vnode vlabel Label associated with vp Determine whether the subject should be allowed to load the specified module file. <function>&mac.mpo;_check_kld_stat</function> int &mac.mpo;_check_kld_stat struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to retrieve a list of loaded kernel module files and associated statistics. <function>&mac.mpo;_check_kld_unload</function> int &mac.mpo;_check_kld_unload struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to unload a kernel module. <function>&mac.mpo;_check_pipe_ioctl</function> int &mac.mpo;_check_pipe_ioctl struct ucred *cred struct pipe *pipe struct label *pipelabel unsigned long cmd void *data &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe cmd &man.ioctl.2; command data &man.ioctl.2; data Determine whether the subject should be allowed to make the specified &man.ioctl.2; call. <function>&mac.mpo;_check_pipe_poll</function> int &mac.mpo;_check_pipe_poll struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed to poll pipe. <function>&mac.mpo;_check_pipe_read</function> int &mac.mpo;_check_pipe_read struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed read access to pipe. <function>&mac.mpo;_check_pipe_relabel</function> int &mac.mpo;_check_pipe_relabel struct ucred *cred struct pipe *pipe struct label *pipelabel struct label *newlabel &mac.thead; cred Subject credential pipe Pipe pipelabel Current policy label associated with pipe newlabel Label update to pipelabel Determine whether the subject should be allowed to relabel pipe. <function>&mac.mpo;_check_pipe_stat</function> int &mac.mpo;_check_pipe_stat struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed to retrieve statistics related to pipe. <function>&mac.mpo;_check_pipe_write</function> int &mac.mpo;_check_pipe_write struct ucred *cred struct pipe *pipe struct label *pipelabel &mac.thead; cred Subject credential pipe Pipe pipelabel Policy label associated with pipe Determine whether the subject should be allowed to write to pipe. <function>&mac.mpo;_check_socket_bind</function> int &mac.mpo;_check_socket_bind struct ucred *cred struct socket *socket struct label *socketlabel struct sockaddr *sockaddr &mac.thead; cred Subject credential socket Socket to be bound socketlabel Policy label for socket sockaddr Address of socket <function>&mac.mpo;_check_socket_connect</function> int &mac.mpo;_check_socket_connect struct ucred *cred struct socket *socket struct label *socketlabel struct sockaddr *sockaddr &mac.thead; cred Subject credential socket Socket to be connected socketlabel Policy label for socket sockaddr Address of socket Determine whether the subject credential (cred) can connect the passed socket (socket) to the passed socket address (sockaddr). Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches, EPERM for lack of privilege. <function>&mac.mpo;_check_socket_receive</function> int &mac.mpo;_check_socket_receive struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential so Socket socketlabel Policy label associated with so Determine whether the subject should be allowed to receive information from the socket so. <function>&mac.mpo;_check_socket_send</function> int &mac.mpo;_check_socket_send struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential so Socket socketlabel Policy label associated with so Determine whether the subject should be allowed to send information across the socket so. <function>&mac.mpo;_check_cred_visible</function> int &mac.mpo;_check_cred_visible struct ucred *u1 struct ucred *u2 &mac.thead; u1 Subject credential u2 Object credential Determine whether the subject credential u1 can see other subjects with the passed subject credential u2. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches, EPERM for lack of privilege, or ESRCH to hide visibility. This call may be made in a number of situations, including inter-process status sysctls used by ps, and in procfs lookups. <function>&mac.mpo;_check_socket_visible</function> int &mac.mpo;_check_socket_visible struct ucred *cred struct socket *socket struct label *socketlabel &mac.thead; cred Subject credential socket Object; socket socketlabel Policy label for socket <function>&mac.mpo;_check_ifnet_relabel</function> int &mac.mpo;_check_ifnet_relabel struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct label *newlabel &mac.thead; cred Subject credential ifnet Object; network interface ifnetlabel Existing policy label for ifnet newlabel Policy label update to later be applied to ifnet Determine whether the subject credential can relabel the passed network interface to the passed label update. <function>&mac.mpo;_check_socket_relabel</function> int &mac.mpo;_check_socket_relabel struct ucred *cred struct socket *socket struct label *socketlabel struct label *newlabel &mac.thead; cred Subject credential socket Object; socket socketlabel Existing policy label for socket newlabel Label update to later be applied to socketlabel Determine whether the subject credential can relabel the passed socket to the passed label update. <function>&mac.mpo;_check_cred_relabel</function> int &mac.mpo;_check_cred_relabel struct ucred *cred struct label *newlabel &mac.thead; cred Subject credential newlabel Label update to later be applied to cred Determine whether the subject credential can relabel itself to the passed label update. <function>&mac.mpo;_check_vnode_relabel</function> int &mac.mpo;_check_vnode_relabel struct ucred *cred struct vnode *vp struct label *vnodelabel struct label *newlabel &mac.thead; cred Subject credential Immutable vp Object; vnode Locked vnodelabel Existing policy label for vp newlabel Policy label update to later be applied to vp Determine whether the subject credential can relabel the passed vnode to the passed label update. <function>&mac.mpo;_check_mount_stat</function> int &mac.mpo;_check_mount_stat struct ucred *cred struct mount *mp struct label *mountlabel &mac.thead; cred Subject credential mp Object; file system mount mountlabel Policy label for mp Determine whether the subject credential can see the results of a statfs performed on the file system. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches or EPERM for lack of privilege. This call may be made in a number of situations, including during invocations of &man.statfs.2; and related calls, as well as to determine what file systems to exclude from listings of file systems, such as when &man.getfsstat.2; is invoked. <function>&mac.mpo;_check_proc_debug</function> int &mac.mpo;_check_proc_debug struct ucred *cred struct proc *proc &mac.thead; cred Subject credential Immutable proc Object; process Determine whether the subject credential can debug the passed process. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, EPERM for lack of privilege, or ESRCH to hide visibility of the target. This call may be made in a number of situations, including use of the &man.ptrace.2; and &man.ktrace.2; APIs, as well as for some types of procfs operations. <function>&mac.mpo;_check_vnode_access</function> int &mac.mpo;_check_vnode_access struct ucred *cred struct vnode *vp struct label *label int flags &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp flags &man.access.2; flags Determine how invocations of &man.access.2; and related calls by the subject credential should return when performed on the passed vnode using the passed access flags. This should generally be implemented using the same semantics used in &mac.mpo;_check_vnode_open. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_chdir</function> int &mac.mpo;_check_vnode_chdir struct ucred *cred struct vnode *dvp struct label *dlabel &mac.thead; cred Subject credential dvp Object; vnode to &man.chdir.2; into dlabel Policy label for dvp Determine whether the subject credential can change the process working directory to the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_chroot</function> int &mac.mpo;_check_vnode_chroot struct ucred *cred struct vnode *dvp struct label *dlabel &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp Determine whether the subject should be allowed to &man.chroot.2; into the specified directory (dvp). <function>&mac.mpo;_check_vnode_create</function> int &mac.mpo;_check_vnode_create struct ucred *cred struct vnode *dvp struct label *dlabel struct componentname *cnp struct vattr *vap &mac.thead; cred Subject credential dvp Object; vnode dlabel Policy label for dvp cnp Component name for dvp vap vnode attributes for vap Determine whether the subject credential can create a vnode with the passed parent directory, passed name information, and passed attribute information. Return 0 for success, or an errno value for failure. Suggested failure: EACCES. for label mismatch, or EPERM for lack of privilege. This call may be made in a number of situations, including as a result of calls to &man.open.2; with O_CREAT, &man.mknod.2;, &man.mkfifo.2;, and others. <function>&mac.mpo;_check_vnode_delete</function> int &mac.mpo;_check_vnode_delete struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp void *label struct componentname *cnp &mac.thead; cred Subject credential dvp Parent directory vnode dlabel Policy label for dvp vp Object; vnode to delete label Policy label for vp cnp Component name for vp Determine whether the subject credential can delete a vnode from the passed parent directory and passed name information. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. This call may be made in a number of situations, including as a result of calls to &man.unlink.2; and &man.rmdir.2;. Policies implementing this entry point should also implement mpo_check_rename_to to authorize deletion of objects as a result of being the target of a rename. <function>&mac.mpo;_check_vnode_deleteacl</function> int &mac.mpo;_check_vnode_deleteacl struct ucred *cred struct vnode *vp struct label *label acl_type_t type &mac.thead; cred Subject credential Immutable vp Object; vnode Locked label Policy label for vp type ACL type Determine whether the subject credential can delete the ACL of passed type from the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_exec</function> int &mac.mpo;_check_vnode_exec struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode to execute label Policy label for vp Determine whether the subject credential can execute the passed vnode. Determination of execute privilege is made separately from decisions about any transitioning event. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_getacl</function> int &mac.mpo;_check_vnode_getacl struct ucred *cred struct vnode *vp struct label *label acl_type_t type &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp type ACL type Determine whether the subject credential can retrieve the ACL of passed type from the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_getextattr</function> int &mac.mpo;_check_vnode_getextattr struct ucred *cred struct vnode *vp struct label *label int attrnamespace const char *name struct uio *uio &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp attrnamespace Extended attribute namespace name Extended attribute name uio I/O structure pointer; see &man.uio.9; Determine whether the subject credential can retrieve the extended attribute with the passed namespace and name from the passed vnode. Policies implementing labeling using extended attributes may be interested in special handling of operations on those extended attributes. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_link</function> int &mac.mpo;_check_vnode_link struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp struct label *label struct componentname *cnp &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp vp Link destination vnode label Policy label associated with vp cnp Component name for the link being created Determine whether the subject should be allowed to create a link to the vnode vp with the name specified by cnp. <function>&mac.mpo;_check_vnode_mmap</function> int &mac.mpo;_check_vnode_mmap struct ucred *cred struct vnode *vp struct label *label int prot &mac.thead; cred Subject credential vp Vnode to map label Policy label associated with vp prot Mmap protections (see &man.mmap.2;) Determine whether the subject should be allowed to map the vnode vp with the protections specified in prot. <function>&mac.mpo;_check_vnode_mmap_downgrade</function> void &mac.mpo;_check_vnode_mmap_downgrade struct ucred *cred struct vnode *vp struct label *label int *prot &mac.thead; cred See . vp label prot Mmap protections to be downgraded Downgrade the mmap protections based on the subject and object labels. <function>&mac.mpo;_check_vnode_mprotect</function> int &mac.mpo;_check_vnode_mprotect struct ucred *cred struct vnode *vp struct label *label int prot &mac.thead; cred Subject credential vp Mapped vnode prot Memory protections Determine whether the subject should be allowed to set the specified memory protections on memory mapped from the vnode vp. <function>&mac.mpo;_check_vnode_poll</function> int &mac.mpo;_check_vnode_poll struct ucred *active_cred struct ucred *file_cred struct vnode *vp struct label *label &mac.thead; active_cred Subject credential file_cred Credential associated with the struct file vp Polled vnode label Policy label associated with vp Determine whether the subject should be allowed to poll the vnode vp. <function>&mac.mpo;_check_vnode_rename_from</function> int &mac.mpo;_vnode_rename_from struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp struct label *label struct componentname *cnp &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp vp Vnode to be renamed label Policy label associated with vp cnp Component name for vp Determine whether the subject should be allowed to rename the vnode vp to something else. <function>&mac.mpo;_check_vnode_rename_to</function> int &mac.mpo;_check_vnode_rename_to struct ucred *cred struct vnode *dvp struct label *dlabel struct vnode *vp struct label *label int samedir struct componentname *cnp &mac.thead; cred Subject credential dvp Directory vnode dlabel Policy label associated with dvp vp Overwritten vnode label Policy label associated with vp samedir Boolean; 1 if the source and destination directories are the same cnp Destination component name Determine whether the subject should be allowed to rename to the vnode vp, into the directory dvp, or to the name represented by cnp. If there is no existing file to overwrite, vp and label will be NULL. <function>&mac.mpo;_check_socket_listen</function> int &mac.mpo;_check_socket_listen struct ucred *cred struct socket *socket struct label *socketlabel &mac.thead; cred Subject credential socket Object; socket socketlabel Policy label for socket Determine whether the subject credential can listen on the passed socket. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_lookup</function> int &mac.mpo;_check_vnode_lookup struct ucred *cred struct vnode *dvp struct label *dlabel struct componentname *cnp &mac.thead; cred Subject credential dvp Object; vnode dlabel Policy label for dvp cnp Component name being looked up Determine whether the subject credential can perform a lookup in the passed directory vnode for the passed name. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_open</function> int &mac.mpo;_check_vnode_open struct ucred *cred struct vnode *vp struct label *label int acc_mode &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp acc_mode &man.open.2; access mode Determine whether the subject credential can perform an open operation on the passed vnode with the passed access mode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_readdir</function> int &mac.mpo;_check_vnode_readdir struct ucred *cred struct vnode *dvp struct label *dlabel &mac.thead; cred Subject credential dvp Object; directory vnode dlabel Policy label for dvp Determine whether the subject credential can perform a readdir operation on the passed directory vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_readlink</function> int &mac.mpo;_check_vnode_readlink struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp Determine whether the subject credential can perform a readlink operation on the passed symlink vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. This call may be made in a number of situations, including an explicit readlink call by the user process, or as a result of an implicit readlink during a name lookup by the process. <function>&mac.mpo;_check_vnode_revoke</function> int &mac.mpo;_check_vnode_revoke struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp Determine whether the subject credential can revoke access to the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setacl</function> int &mac.mpo;_check_vnode_setacl struct ucred *cred struct vnode *vp struct label *label acl_type_t type struct acl *acl &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp type ACL type acl ACL Determine whether the subject credential can set the passed ACL of passed type on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setextattr</function> int &mac.mpo;_check_vnode_setextattr struct ucred *cred struct vnode *vp struct label *label int attrnamespace const char *name struct uio *uio &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp attrnamespace Extended attribute namespace name Extended attribute name uio I/O structure pointer; see &man.uio.9; Determine whether the subject credential can set the extended attribute of passed name and passed namespace on the passed vnode. Policies implementing security labels backed into extended attributes may want to provide additional protections for those attributes. Additionally, policies should avoid making decisions based on the data referenced from uio, as there is a potential race condition between this check and the actual operation. The uio may also be NULL if a delete operation is being performed. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setflags</function> int &mac.mpo;_check_vnode_setflags struct ucred *cred struct vnode *vp struct label *label u_long flags &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp flags File flags; see &man.chflags.2; Determine whether the subject credential can set the passed flags on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setmode</function> int &mac.mpo;_check_vnode_setmode struct ucred *cred struct vnode *vp struct label *label mode_t mode &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp mode File mode; see &man.chmod.2; Determine whether the subject credential can set the passed mode on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setowner</function> int &mac.mpo;_check_vnode_setowner struct ucred *cred struct vnode *vp struct label *label uid_t uid gid_t gid &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp uid User ID gid Group ID Determine whether the subject credential can set the passed uid and passed gid as file uid and file gid on the passed vnode. The IDs may be set to (-1) to request no update. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_vnode_setutimes</function> int &mac.mpo;_check_vnode_setutimes struct ucred *cred struct vnode *vp struct label *label struct timespec atime struct timespec mtime &mac.thead; cred Subject credential vp Object; vp label Policy label for vp atime Access time; see &man.utimes.2; mtime Modification time; see &man.utimes.2; Determine whether the subject credential can set the passed access timestamps on the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_proc_sched</function> int &mac.mpo;_check_proc_sched struct ucred *ucred struct proc *proc &mac.thead; cred Subject credential proc Object; process Determine whether the subject credential can change the scheduling parameters of the passed process. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, EPERM for lack of privilege, or ESRCH to limit visibility. See &man.setpriority.2; for more information. <function>&mac.mpo;_check_proc_signal</function> int &mac.mpo;_check_proc_signal struct ucred *cred struct proc *proc int signal &mac.thead; cred Subject credential proc Object; process signal Signal; see &man.kill.2; Determine whether the subject credential can deliver the passed signal to the passed process. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, EPERM for lack of privilege, or ESRCH to limit visibility. <function>&mac.mpo;_check_vnode_stat</function> int &mac.mpo;_check_vnode_stat struct ucred *cred struct vnode *vp struct label *label &mac.thead; cred Subject credential vp Object; vnode label Policy label for vp Determine whether the subject credential can stat the passed vnode. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. See &man.stat.2; for more information. <function>&mac.mpo;_check_ifnet_transmit</function> int &mac.mpo;_check_ifnet_transmit struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; cred Subject credential ifnet Network interface ifnetlabel Policy label for ifnet mbuf Object; mbuf to be sent mbuflabel Policy label for mbuf Determine whether the network interface can transmit the passed mbuf. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_socket_deliver</function> int &mac.mpo;_check_socket_deliver struct ucred *cred struct ifnet *ifnet struct label *ifnetlabel struct mbuf *mbuf struct label *mbuflabel &mac.thead; cred Subject credential ifnet Network interface ifnetlabel Policy label for ifnet mbuf Object; mbuf to be delivered mbuflabel Policy label for mbuf Determine whether the socket may receive the datagram stored in the passed mbuf header. Return 0 for success, or an errno value for failure. Suggested failures: EACCES for label mismatch, or EPERM for lack of privilege. <function>&mac.mpo;_check_socket_visible</function> int &mac.mpo;_check_socket_visible struct ucred *cred struct socket *so struct label *socketlabel &mac.thead; cred Subject credential Immutable so Object; socket socketlabel Policy label for so Determine whether the subject credential cred can "see" the passed socket (socket) using system monitoring functions, such as those employed by &man.netstat.8; and &man.sockstat.1;. Return 0 for success, or an errno value for failure. Suggested failure: EACCES for label mismatches, EPERM for lack of privilege, or ESRCH to hide visibility. <function>&mac.mpo;_check_system_acct</function> int &mac.mpo;_check_system_acct struct ucred *ucred struct vnode *vp struct label *vlabel &mac.thead; ucred Subject credential vp Accounting file; &man.acct.5; vlabel Label associated with vp Determine whether the subject should be allowed to enable accounting, based on its label and the label of the accounting log file. <function>&mac.mpo;_check_system_nfsd</function> int &mac.mpo;_check_system_nfsd struct ucred *cred &mac.thead; cred Subject credential Determine whether the subject should be allowed to call &man.nfssvc.2;. <function>&mac.mpo;_check_system_reboot</function> int &mac.mpo;_check_system_reboot struct ucred *cred int howto &mac.thead; cred Subject credential howto howto parameter from &man.reboot.2; Determine whether the subject should be allowed to reboot the system in the specified manner. <function>&mac.mpo;_check_system_settime</function> int &mac.mpo;_check_system_settime struct ucred *cred &mac.thead; cred Subject credential Determine whether the user should be allowed to set the system clock. <function>&mac.mpo;_check_system_swapon</function> int &mac.mpo;_check_system_swapon struct ucred *cred struct vnode *vp struct label *vlabel &mac.thead; cred Subject credential vp Swap device vlabel Label associated with vp Determine whether the subject should be allowed to add vp as a swap device. <function>&mac.mpo;_check_system_sysctl</function> int &mac.mpo;_check_system_sysctl struct ucred *cred int *name u_int *namelen void *old size_t *oldlenp int inkernel void *new size_t newlen &mac.thead; cred Subject credential name See &man.sysctl.3; namelen old oldlenp inkernel Boolean; 1 if called from kernel new See &man.sysctl.3; newlen Determine whether the subject should be allowed to make the specified &man.sysctl.3; transaction. Label Management Calls Relabel events occur when a user process has requested that the label on an object be modified. A two-phase update occurs: first, an access control check will be performed to determine if the update is both valid and permitted, and then the update itself is performed via a separate entry point. Relabel entry points typically accept the object, object label reference, and an update label submitted by the process. Memory allocation during relabel is discouraged, as relabel calls are not permitted to fail (failure should be reported earlier in the relabel check). Userland Architecture The TrustedBSD MAC Framework includes a number of policy-agnostic elements, including MAC library interfaces for abstractly managing labels, modifications to the system credential management and login libraries to support the assignment of MAC labels to users, and a set of tools to monitor and modify labels on processes, files, and network interfaces. More details on the user architecture will be added to this section in the near future. APIs for Policy-Agnostic Label Management The TrustedBSD MAC Framework provides a number of library and system calls permitting applications to manage MAC labels on objects using a policy-agnostic interface. This permits applications to manipulate labels for a variety of policies without being written to support specific policies. These interfaces are used by general-purpose tools such as &man.ifconfig.8;, &man.ls.1; and &man.ps.1; to view labels on network interfaces, files, and processes. The APIs also support MAC management tools including &man.getfmac.8;, &man.getpmac.8;, &man.setfmac.8;, &man.setfsmac.8;, and &man.setpmac.8;. The MAC APIs are documented in &man.mac.3;. Applications handle MAC labels in two forms: an internalized form used to return and set labels on processes and objects (mac_t), and externalized form based on C strings appropriate for storage in configuration files, display to the user, or input from the user. Each MAC label contains a number of elements, each consisting of a name and value pair. Policy modules in the kernel bind to specific names and interpret the values in policy-specific ways. In the externalized string form, labels are represented by a comma-delimited list of name and value pairs separated by the / character. Labels may be directly converted to and from text using provided APIs; when retrieving labels from the kernel, internalized label storage must first be prepared for the desired label element set. Typically, this is done in one of two ways: using &man.mac.prepare.3; and an arbitrary list of desired label elements, or one of the variants of the call that loads a default element set from the &man.mac.conf.5; configuration file. Per-object defaults permit application writers to usefully display labels associated with objects without being aware of the policies present in the system. Currently, direct manipulation of label elements other than by conversion to a text string, string editing, and conversion back to an internalized label is not supported by the MAC library. Such interfaces may be added in the future if they prove necessary for application writers. Binding of Labels to Users The standard user context management interface, &man.setusercontext.3;, has been modified to retrieve MAC labels associated with a user's class from &man.login.conf.5;. These labels are then set along with other user context when either LOGIN_SETALL is specified, or when LOGIN_SETMAC is explicitly specified. It is expected that, in a future version of FreeBSD, the MAC label database will be separated from the login.conf user class abstraction, and be maintained in a separate database. However, the &man.setusercontext.3; API should remain the same following such a change. Conclusion The TrustedBSD MAC framework permits kernel modules to augment the system security policy in a highly integrated manner. They may do this based on existing object properties, or based on label data that is maintained with the assistance of the MAC framework. The framework is sufficiently flexible to implement a variety of policy types, including information flow security policies such as MLS and Biba, as well as policies based on existing BSD credentials or file protections. Policy authors may wish to consult this documentation as well as existing security modules when implementing a new security service.
diff --git a/en_US.ISO8859-1/books/developers-handbook/newbus/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/newbus/chapter.sgml index 4fda867ee6..c20617c4fc 100644 --- a/en_US.ISO8859-1/books/developers-handbook/newbus/chapter.sgml +++ b/en_US.ISO8859-1/books/developers-handbook/newbus/chapter.sgml @@ -1,360 +1,360 @@ Jeroen Ruigrok van der Werven (asmodai)
asmodai@FreeBSD.org
Written by
Hiten Pandya
hiten@uk.FreeBSD.org
Newbus Special thanks to Matthew N. Dodd, Warner Losh, Bill Paul, Doug Rabson, Mike Smith, Peter Wemm and Scott Long. This chapter explains the Newbus device framework in detail. Device Drivers Purpose of a Device Driver A device driver is a software component which provides the interface between the kernel's generic view of a peripheral (e.g. disk, network adapter) and the actual implementation of the peripheral. The device driver interface (DDI) is the defined interface between the kernel and the device driver component. Types of Device Drivers There used to be days in &unix;, and thus FreeBSD, in which there were four types of devices defined: block device drivers character device drivers network device drivers pseudo-device drivers Block devices performed in way that used fixed size blocks [of data]. This type of driver depended on the so called buffer cache, which had the purpose to cache accessed blocks of data in a dedicated part of the memory. Often this buffer cache was based on write-behind, which meant that when data was modified in memory it got synced to disk whenever the system did its periodical disk flushing, thus optimizing writes. Character devices However, in the versions of FreeBSD 4.0 and onward the distinction between block and character devices became non-existent. Overview of Newbus Newbus is the implementation of a new bus architecture based on abstraction layers which saw its introduction in FreeBSD 3.0 when the Alpha port was imported into the source tree. It was not until 4.0 before it became the default system to use for device drivers. Its goals are to provide a more object oriented means of interconnecting the various busses and devices which a host system provides to the Operating System. Its main features include amongst others: dynamic attaching easy modularization of drivers pseudo-busses One of the most prominent changes is the migration from the flat and ad-hoc system to a device tree lay-out. At the top level resides the root device which is the parent to hang all other devices on. For each architecture, there is typically a single child of root which has such things as host-to-PCI bridges, etc. attached to it. For x86, this root device is the nexus device and for Alpha, various different different models of Alpha have different top-level devices corresponding to the different hardware chipsets, including lca, apecs, cia and tsunami. A device in the Newbus context represents a single hardware entity in the system. For instance each PCI device is represented by a Newbus device. Any device in the system can have children; a device which has children is often called a bus. Examples of common busses in the system are ISA and PCI which manage lists of devices attached to ISA and PCI busses respectively. Often, a connection between different kinds of bus is represented by a bridge device which normally has one child for the attached bus. An example of this is a PCI-to-PCI bridge which is represented by a device pcibN on the parent PCI bus and has a child pciN for the attached bus. This layout simplifies the implementation of the PCI bus tree, allowing common code to be used for both top-level and bridged busses. Each device in the Newbus architecture asks its parent to map its resources. The parent then asks its own parent until the nexus is reached. So, basically the nexus is the only part of the Newbus system which knows about all resources. An ISA device might want to map its IO port at 0x230, so it asks its parent, in this case the ISA bus. The ISA bus hands it over to the PCI-to-ISA bridge which in its turn asks the PCI bus, which reaches the host-to-PCI bridge and finally the nexus. The beauty of this transition upwards is that there is room to translate the requests. For example, the 0x230 IO port request might become memory-mapped at 0xb0000230 on a MIPS box by the PCI bridge. Resource allocation can be controlled at any place in the device tree. For instance on many Alpha platforms, ISA interrupts are managed separately from PCI interrupts and resource allocations for ISA interrupts are managed by the Alpha's ISA bus device. On IA-32, ISA and PCI interrupts are both managed by the top-level nexus device. For both ports, memory and port address space is managed by a single entity - nexus for IA-32 and the relevant chipset driver on Alpha (e.g. CIA or tsunami). In order to normalize access to memory and port mapped resources, Newbus integrates the bus_space APIs from NetBSD. These provide a single API to replace inb/outb and direct memory reads/writes. The advantage of this is that a single driver can easily use either memory-mapped registers or port-mapped registers (some hardware supports both). This support is integrated into the resource allocation mechanism. When a resource is allocated, a driver can retrieve the associated bus_space_tag_t and bus_space_handle_t from the resource. Newbus also allows for definitions of interface methods in files dedicated to this purpose. These are the .m files that are found under the src/sys hierarchy. The core of the Newbus system is an extensible object-based programming model. Each device in the system has a table of methods which it supports. The system and other devices uses those methods to control the device and request services. The different methods supported by a device are defined by a number of interfaces. An interface is simply a group of related methods which can be implemented by a device. In the Newbus system, the methods for a device are provided by the various device drivers in the system. When a device is attached to a driver during auto-configuration, it uses the method table declared by the driver. A device can later detach from its driver and re-attach to a new driver with a new method table. This allows dynamic replacement of drivers which can be useful for driver development. The interfaces are described by an interface definition language similar to the language used to define vnode operations for file systems. The interface would be stored in a methods file (which would normally named foo_if.m). Newbus Methods # Foo subsystem/driver (a comment...) INTERFACE foo METHOD int doit { device_t dev; }; # DEFAULT is the method that will be used, if a method was not # provided via: DEVMETHOD() METHOD void doit_to_child { device_t dev; driver_t child; } DEFAULT doit_generic_to_child; When this interface is compiled, it generates a header file foo_if.h which contains function declarations: int FOO_DOIT(device_t dev); int FOO_DOIT_TO_CHILD(device_t dev, device_t child); A source file, foo_if.c is also created to accompany the automatically generated header file; it contains implementations of those functions which look up the location of the relevant functions in the object's method table and call that function. The system defines two main interfaces. The first fundamental interface is called device and includes methods which are relevant to all devices. Methods in the device interface include probe, attach and detach to control detection of hardware and shutdown, suspend and resume for critical event notification. The second, more complex interface is bus. This interface contains methods suitable for devices which have children, including methods to access bus specific per-device information &man.bus.generic.read.ivar.9; and &man.bus.generic.write.ivar.9;, event notification (child_detached, driver_added) and resource management (alloc_resource, activate_resource, deactivate_resource, - release_resource). + release_resource). Many methods in the bus interface are performing services for some child of the bus device. These methods would normally use the first two arguments to specify the bus providing the service and the child device which is requesting the service. To simplify driver code, many of these methods have accessor functions which lookup the parent and call a method on the parent. For instance the method BUS_TEARDOWN_INTR(device_t dev, device_t child, ...) can be called using the function bus_teardown_intr(device_t child, ...). Some bus types in the system define additional interfaces to provide access to bus-specific functionality. For instance, the PCI bus driver defines the pci interface which has two methods read_config and write_config for accessing the configuration registers of a PCI device. Newbus API As the Newbus API is huge, this section makes some effort at documenting it. More information to come in the next revision of this document. Important locations in the source hierarchy src/sys/[arch]/[arch] - Kernel code for a specific machine architecture resides in this directory. for example, the i386 architecture, or the SPARC64 architecture. src/sys/dev/[bus] - device support for a specific [bus] resides in this directory. src/sys/dev/pci - PCI bus support code resides in this directory. src/sys/[isa|pci] - PCI/ISA device drivers reside in this directory. The PCI/ISA bus support code used to exist in this directory in FreeBSD version 4.0. Important structures and type definitions devclass_t - This is a type definition of a pointer to a struct devclass. device_method_t - This is same as kobj_method_t (see src/sys/kobj.h). device_t - This is a type definition of a pointer to a struct device. device_t represents a device in the system. It is a kernel object. See src/sys/sys/bus_private.h for implementation details. driver_t - This is a type definition which, references struct driver. The driver struct is a class of the device kernel object; it also holds data private to for the driver.
<emphasis>driver_t</emphasis> implementation struct driver { KOBJ_CLASS_FIELDS; void *priv; /* driver private data */ };
A device_state_t type, which is an enumeration, device_state. It contains the possible states of a Newbus device before and after the autoconfiguration process.
Device states<emphasis>device_state_t</emphasis> /* * src/sys/sys/bus.h */ typedef enum device_state { DS_NOTPRESENT, /* not probed or probe failed */ DS_ALIVE, /* probe succeeded */ DS_ATTACHED, /* attach method called */ DS_BUSY /* device is open */ } device_state_t;
diff --git a/en_US.ISO8859-1/books/developers-handbook/sound/chapter.sgml b/en_US.ISO8859-1/books/developers-handbook/sound/chapter.sgml index aeb4656017..0a107812fa 100644 --- a/en_US.ISO8859-1/books/developers-handbook/sound/chapter.sgml +++ b/en_US.ISO8859-1/books/developers-handbook/sound/chapter.sgml @@ -1,687 +1,690 @@ Jean-Francois Dockes Contributed by Sound subsystem Introduction The FreeBSD sound subsystem cleanly separates generic sound handling issues from device-specific ones. This makes it easier to add support for new hardware. The &man.pcm.4; framework is the central piece of the sound subsystem. It mainly implements the following elements: A system call interface (read, write, ioctls) to digitized sound and mixer functions. The ioctl command set is compatible with the legacy OSS or Voxware interface, allowing common multimedia applications to be ported without modification. + Common code for processing sound data (format conversions, virtual channels). A uniform software interface to hardware-specific audio interface modules. Additional support for some common hardware interfaces (ac97), or shared hardware-specific code (ex: ISA DMA routines). The support for specific sound cards is implemented by hardware-specific drivers, which provide channel and mixer interfaces to plug into the generic pcm code. In this chapter, the term pcm will refer to the central, common part of the sound driver, as opposed to the hardware-specific modules. The prospective driver writer will of course want to start from an existing module and use the code as the ultimate reference. But, while the sound code is nice and clean, it is also mostly devoid of comments. This document tries to give an overview of the framework interface and answer some questions that may arise while adapting the existing code. As an alternative, or in addition to starting from a working example, you can find a commented driver template at http://people.FreeBSD.org/~cg/template.c Files All the relevant code currently (FreeBSD 4.4) lives in /usr/src/sys/dev/sound/, except for the public ioctl interface definitions, found in /usr/src/sys/sys/soundcard.h Under /usr/src/sys/dev/sound/, the pcm/ directory holds the central code, while the isa/ and pci/ directories have the drivers for ISA and PCI boards. Probing, attaching, etc. Sound drivers probe and attach in almost the same way as any hardware driver module. You might want to look at the ISA or PCI specific sections of the handbook for more information. However, sound drivers differ in some ways: They declare themselves as pcm class devices, with a struct snddev_info device private structure: static driver_t xxx_driver = { "pcm", xxx_methods, sizeof(struct snddev_info) }; DRIVER_MODULE(snd_xxxpci, pci, xxx_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_xxxpci, snd_pcm, PCM_MINVER, PCM_PREFVER,PCM_MAXVER); Most sound drivers need to store additional private information about their device. A private data structure is usually allocated in the attach routine. Its address is passed to pcm by the calls to pcm_register() and mixer_init(). pcm later passes back this address as a parameter in calls to the sound driver interfaces. The sound driver attach routine should declare its MIXER or AC97 interface to pcm by calling mixer_init(). For a MIXER interface, this causes in turn a call to xxxmixer_init(). The sound driver attach routine declares its general CHANNEL configuration to pcm by calling pcm_register(dev, sc, nplay, nrec), where sc is the address for the device data structure, used in further calls from pcm, and nplay and nrec are the number of play and record channels. The sound driver attach routine declares each of its channel objects by calls to pcm_addchan(). This sets up the channel glue in pcm and causes in turn a call to xxxchannel_init(). The sound driver detach routine should call pcm_unregister() before releasing its resources. There are two possible methods to handle non-PnP devices: Use a device_identify() method (example: sound/isa/es1888.c). The device_identify() method probes for the hardware at known addresses and, if it finds a supported device, creates a new pcm device which is then passed to probe/attach. Use a custom kernel configuration with appropriate hints for pcm devices (example: sound/isa/mss.c). pcm drivers should implement device_suspend, device_resume and device_shutdown routines, so that power management and module unloading function correctly. Interfaces The interface between the pcm core and the sound drivers is defined in terms of kernel objects. There are two main interfaces that a sound driver will usually provide: CHANNEL and either MIXER or AC97. The AC97 interface is a very small hardware access (register read/write) interface, implemented by drivers for hardware with an AC97 codec. In this case, the actual MIXER interface is provided by the shared AC97 code in pcm. The CHANNEL interface Common notes for function parameters Sound drivers usually have a private data structure to describe their device, and one structure for each play and record data channel that it supports. For all CHANNEL interface functions, the first parameter is an opaque pointer. The second parameter is a pointer to the private channel data structure, except for channel_init() which has a pointer to the private device structure (and returns the channel pointer for further use by pcm). Overview of data transfer operations For sound data transfers, the pcm core and the sound drivers communicate through a shared memory area, described by a struct snd_dbuf. struct snd_dbuf is private to pcm, and sound drivers obtain values of interest by calls to accessor functions (sndbuf_getxxx()). The shared memory area has a size of sndbuf_getsize() and is divided into fixed size blocks of sndbuf_getblksz() bytes. When playing, the general transfer mechanism is as follows (reverse the idea for recording): pcm initially fills up the buffer, then calls the sound driver's xxxchannel_trigger() function with a parameter of PCMTRIG_START. The sound driver then arranges to repeatedly transfer the whole memory area (sndbuf_getbuf(), sndbuf_getsize()) to the device, in blocks of sndbuf_getblksz() bytes. It calls back the chn_intr() pcm function for each transferred block (this will typically happen at interrupt time). chn_intr() arranges to copy new data to the area that was transferred to the device (now free), and make appropriate updates to the snd_dbuf structure. + + channel_init xxxchannel_init() is called to initialize each of the play or record channels. The calls are initiated from the sound driver attach routine. (See the probe and attach section). static void * xxxchannel_init(kobj_t obj, void *data, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct xxx_info *sc = data; struct xxx_chinfo *ch; ... return ch; } b is the address for the channel struct snd_dbuf. It should be initialized in the function by calling sndbuf_alloc(). The buffer size to use is normally a small multiple of the 'typical' unit transfer size for your device. c is the pcm channel control structure pointer. This is an opaque object. The function should store it in the local channel structure, to be used in later calls to pcm (ie: chn_intr(c)). dir indicates the channel direction (PCMDIR_PLAY or PCMDIR_REC). The function should return a pointer to the private area used to control this channel. This will be passed as a parameter to other channel interface calls. channel_setformat xxxchannel_setformat() should set up the hardware for the specified channel for the specified sound format. static int xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format) { struct xxx_chinfo *ch = data; ... return 0; } format is specified as an AFMT_XXX value (soundcard.h). channel_setspeed xxxchannel_setspeed() sets up the channel hardware for the specified sampling speed, and returns the possibly adjusted speed. static int xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct xxx_chinfo *ch = data; ... return speed; } channel_setblocksize xxxchannel_setblocksize() sets the block size, which is the size of unit transactions between pcm and the sound driver, and between the sound driver and the device. Typically, this would be the number of bytes transferred before an interrupt occurs. During a transfer, the sound driver should call pcm's chn_intr() every time this size has been transferred. Most sound drivers only take note of the block size here, to be used when an actual transfer will be started. static int xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct xxx_chinfo *ch = data; ... return blocksize; } The function returns the possibly adjusted block size. In case the block size is indeed changed, sndbuf_resize() should be called to adjust the buffer. channel_trigger xxxchannel_trigger() is called by pcm to control data transfer operations in the driver. static int xxxchannel_trigger(kobj_t obj, void *data, int go) { struct xxx_chinfo *ch = data; ... return 0; } go defines the action for the current call. The possible values are: PCMTRIG_START: the driver should start a data transfer from or to the channel buffer. If needed, the buffer base and size can be retrieved through sndbuf_getbuf() and sndbuf_getsize(). PCMTRIG_EMLDMAWR / PCMTRIG_EMLDMARD: this tells the driver that the input or output buffer may have been updated. Most drivers just ignore these calls. PCMTRIG_STOP / PCMTRIG_ABORT: the driver should stop the current transfer. If the driver uses ISA DMA, sndbuf_isadma() should be called before performing actions on the device, and will take care of the DMA chip side of things. channel_getptr xxxchannel_getptr() returns the current offset in the transfer buffer. This will typically be called by chn_intr(), and this is how pcm knows where it can transfer new data. channel_free xxxchannel_free() is called to free up channel resources, for example when the driver is unloaded, and should be implemented if the channel data structures are dynamically allocated or if sndbuf_alloc() was not used for buffer allocation. channel_getcaps struct pcmchan_caps * xxxchannel_getcaps(kobj_t obj, void *data) { return &xxx_caps; } The routine returns a pointer to a (usually statically-defined) pcmchan_caps structure (defined in sound/pcm/channel.h. The structure holds the minimum and maximum sampling frequencies, and the accepted sound formats. Look at any sound driver for an example. More functions channel_reset(), channel_resetdone(), and channel_notify() are for special purposes and should not be implemented in a driver without discussing it with the authorities (&a.cg;). channel_setdir() is deprecated. The MIXER interface mixer_init xxxmixer_init() initializes the hardware and tells pcm what mixer devices are available for playing and recording static int xxxmixer_init(struct snd_mixer *m) { struct xxx_info *sc = mix_getdevinfo(m); u_int32_t v; [Initialize hardware] [Set appropriate bits in v for play mixers] mix_setdevs(m, v); [Set appropriate bits in v for record mixers] mix_setrecdevs(m, v) return 0; } Set bits in an integer value and call mix_setdevs() and mix_setrecdevs() to tell pcm what devices exist. Mixer bits definitions can be found in soundcard.h (SOUND_MASK_XXX values and SOUND_MIXER_XXX bit shifts). mixer_set xxxmixer_set() sets the volume level for one mixer device. static int xxxmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct sc_info *sc = mix_getdevinfo(m); [set volume level] return left | (right << 8); } The device is specified as a SOUND_MIXER_XXX value The volume values are specified in range [0-100]. A value of zero should mute the device. As the hardware levels probably won't match the input scale, and some rounding will occur, the routine returns the actual level values (in range 0-100) as shown. mixer_setrecsrc xxxmixer_setrecsrc() sets the recording source device. static int xxxmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct xxx_info *sc = mix_getdevinfo(m); [look for non zero bit(s) in src, set up hardware] [update src to reflect actual action] return src; } The desired recording devices are specified as a bit field The actual devices set for recording are returned. Some drivers can only set one device for recording. The function should return -1 if an error occurs. mixer_uninit, mixer_reinit xxxmixer_uninit() should ensure that all sound is muted and if possible mixer hardware should be powered down xxxmixer_reinit() should ensure that the mixer hardware is powered up and any settings not controlled by mixer_set() or mixer_setrecsrc() are restored. The AC97 interface The AC97 interface is implemented by drivers with an AC97 codec. It only has three methods: xxxac97_init() returns the number of ac97 codecs found. ac97_read() and ac97_write() read or write a specified register. The AC97 interface is used by the AC97 code in pcm to perform higher level operations. Look at sound/pci/maestro3.c or many others under sound/pci/ for an example.