diff --git a/contrib/hardenedbsd/hbsdcontrol/libhbsdcontrol.c b/contrib/hardenedbsd/hbsdcontrol/libhbsdcontrol.c index 113372a17c87..5d9e96d7b914 100644 --- a/contrib/hardenedbsd/hbsdcontrol/libhbsdcontrol.c +++ b/contrib/hardenedbsd/hbsdcontrol/libhbsdcontrol.c @@ -96,6 +96,13 @@ const struct pax_feature_entry pax_features[] = { [enable] = "hbsd.pax.disallow_map32bit", }, }, + { + .feature = "insecure_kmod", + .extattr = { + [disable] = "hbsd.hardening.permit_kmod", + [enable] = "hbsd.hardening.forbid_kmod", + }, + }, /* Terminating NULL entry, DO NOT REMOVE! */ {NULL, {0, 0}} }; diff --git a/sys/hardenedbsd/hbsd_control_extattr.c b/sys/hardenedbsd/hbsd_control_extattr.c index 0a68a954697c..b8afd50f3fa1 100644 --- a/sys/hardenedbsd/hbsd_control_extattr.c +++ b/sys/hardenedbsd/hbsd_control_extattr.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -54,6 +55,8 @@ FEATURE(hbsd_control_extattr, "HardenedBSD's extattr based control subsystem."); static int pax_control_extattr_status = PAX_FEATURE_SIMPLE_ENABLED; TUNABLE_INT("hardening.control.extattr.status", &pax_control_extattr_status); +static pax_flag_t pax_control_extattr_get_all(struct thread *td, + struct vnode *vp); static bool pax_control_extattr_active(void); struct pax_feature_entry { @@ -74,6 +77,8 @@ const struct pax_feature_entry pax_features[] = { {"hbsd.pax.noshlibrandom", PAX_NOTE_NOSHLIBRANDOM}, {"hbsd.pax.disallow_map32bit", PAX_NOTE_DISALLOWMAP32BIT}, {"hbsd.pax.nodisallow_map32bit", PAX_NOTE_NODISALLOWMAP32BIT}, + {"hbsd.hardening.permit_kmod", PAX_NOTE_PERMITKMOD}, + {"hbsd.hardening.forbid_kmod", PAX_NOTE_FORBIDKMOD}, {NULL, 0} }; @@ -93,6 +98,21 @@ SYSCTL_INT(_hardening_control_extattr, OID_AUTO, status, int pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) +{ + imgp->pax.req_extattr_flags = pax_control_extattr_get_all(td, imgp->vp); + + return (0); +} + +pax_flag_t +pax_control_extattr_kmod(struct thread *td, struct vnode *vp) +{ + return (pax_control_extattr_get_all(td, vp)); +} + +static +pax_flag_t +pax_control_extattr_get_all(struct thread *td, struct vnode *vp) { struct uio uio; struct iovec iov; @@ -107,7 +127,6 @@ pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) int error; if (!pax_control_extattr_active()) { - imgp->pax.req_extattr_flags = 0; return (0); } @@ -121,7 +140,7 @@ pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) feature_present[i] = false; /* Query the size of extended attribute names list. */ - error = VOP_LISTEXTATTR(imgp->vp, EXTATTR_NAMESPACE_SYSTEM, NULL, + error = VOP_LISTEXTATTR(vp, EXTATTR_NAMESPACE_SYSTEM, NULL, &fsea_list_size, NULL, td); /* @@ -131,22 +150,10 @@ pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) * - no FS-EA assigned for the file. */ if (error != 0 || fsea_list_size == 0) { - /* Use system defaults. */ - imgp->pax.req_extattr_flags = 0; - - if (error == EOPNOTSUPP) { - /* - * Use system default, without - * returning an error. - */ - return (0); - } - - return (error); + return (0); } if (fsea_list_size > IOSIZE_MAX) { - error = ENOMEM; goto out; } @@ -165,9 +172,10 @@ pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) uio.uio_resid = fsea_list_size; /* Query the FS-EA list. */ - error = VOP_LISTEXTATTR(imgp->vp, EXTATTR_NAMESPACE_SYSTEM, &uio, NULL, NULL, td); - if (error != 0) + error = VOP_LISTEXTATTR(vp, EXTATTR_NAMESPACE_SYSTEM, &uio, NULL, NULL, td); + if (error != 0) { goto out; + } /* * Create a filter from existing hbsd.pax attributes. @@ -184,8 +192,9 @@ pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) * compare the string's len without the ending zero * with the attribute name stored without zero. */ - if (fsea_attrname_len != entry_size) + if (fsea_attrname_len != entry_size) { continue; + } /* * Compare the strings as byte arrays without the ending zeros @@ -226,7 +235,7 @@ pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) * Use NOCRED as credential to always get the extended attributes, * even if the user execs a program. */ - error = VOP_GETEXTATTR(imgp->vp, EXTATTR_NAMESPACE_SYSTEM, + error = VOP_GETEXTATTR(vp, EXTATTR_NAMESPACE_SYSTEM, pax_features[i].fs_ea_attribute, &uio, NULL, NOCRED, td); if (error == 0) { @@ -261,13 +270,8 @@ pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp) out: free(fsea_list, M_TEMP); - /* In case of error, reset to the system defaults. */ - if (error) - parsed_flags = 0; + return (parsed_flags); - imgp->pax.req_extattr_flags = parsed_flags; - - return (error); } static bool diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c index b06db939c5b8..0dcfbf4b1d4a 100644 --- a/sys/kern/link_elf.c +++ b/sys/kern/link_elf.c @@ -168,10 +168,11 @@ static long link_elf_symtab_get(linker_file_t, const Elf_Sym **); static long link_elf_strtab_get(linker_file_t, caddr_t *); static int elf_lookup(linker_file_t, Elf_Size, int, Elf_Addr *); +static bool link_elf_kmod_is_insecure(linker_file_t lf); static int link_elf_detect_insecure_early(struct thread *td, struct vnode *vp, const char *filename); static int link_elf_detect_insecure_late(struct thread *td, - const char *filename, linker_file_t lf); + const char *filename, struct vnode *vp, linker_file_t lf); static kobj_method_t link_elf_methods[] = { KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), @@ -220,6 +221,15 @@ static struct elf_set_head set_pcpu_list; static struct elf_set_head set_vnet_list; #endif +static bool +link_elf_kmod_is_insecure(linker_file_t lf) +{ + c_linker_sym_t insecure_sym; + + return (link_elf_lookup_symbol(lf, "__insecure_kmod", + &insecure_sym) != ENOENT); +} + static int link_elf_detect_insecure_early(struct thread *td, struct vnode *vp, const char *filename) @@ -230,18 +240,21 @@ link_elf_detect_insecure_early(struct thread *td, struct vnode *vp, static int link_elf_detect_insecure_late(struct thread *td, const char *filename, - linker_file_t lf) + struct vnode *vp, linker_file_t lf) { - c_linker_sym_t insecure_sym; + pax_flag_t flags; - if (link_elf_lookup_symbol(lf, "__insecure_kmod", &insecure_sym) != - ENOENT) { - if (!pax_insecure_kmod()) { - pax_log_internal(td->td_proc, PAX_LOG_P_COMM, - "Insecure kernel module load attempt rejected: %s", - filename != NULL ? filename : ""); - return (EPERM); - } + flags = pax_control_extattr_kmod(td, vp); + if ((flags & PAX_NOTE_PERMITKMOD) == PAX_NOTE_PERMITKMOD) { + return (0); + } + + if ((flags & PAX_NOTE_FORBIDKMOD) == PAX_NOTE_FORBIDKMOD || + (link_elf_kmod_is_insecure(lf) && !pax_insecure_kmod())) { + pax_log_internal(td->td_proc, PAX_LOG_P_COMM, + "Insecure kernel module load attempt rejected: %s", + filename != NULL ? filename : ""); + return (EPERM); } return (0); @@ -1350,7 +1363,7 @@ link_elf_load_file(linker_class_t cls, const char* filename, ef->ddbstrcnt = strcnt; ef->ddbstrtab = ef->strbase; - error = link_elf_detect_insecure_late(td, filename, lf); + error = link_elf_detect_insecure_late(td, filename, nd.ni_vp, lf); if (error != 0) { goto out; } diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c index d31e77af48b9..0484acb756ba 100644 --- a/sys/kern/link_elf_obj.c +++ b/sys/kern/link_elf_obj.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -158,10 +159,11 @@ static long link_elf_strtab_get(linker_file_t, caddr_t *); static int elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *); +static bool link_elf_kmod_is_insecure(linker_file_t lf); static int link_elf_detect_insecure_early(struct thread *td, struct vnode *vp, const char *filename); static int link_elf_detect_insecure_late(struct thread *td, - const char *filename, linker_file_t lf); + const char *filename, struct vnode *vp, linker_file_t lf); static kobj_method_t link_elf_methods[] = { KOBJMETHOD(linker_lookup_symbol, link_elf_lookup_symbol), @@ -199,6 +201,15 @@ SYSCTL_BOOL(_debug, OID_AUTO, link_elf_obj_leak_locals, static int relocate_file(elf_file_t ef); static void elf_obj_cleanup_globals_cache(elf_file_t); +static bool +link_elf_kmod_is_insecure(linker_file_t lf) +{ + c_linker_sym_t insecure_sym; + + return (link_elf_lookup_symbol(lf, "__insecure_kmod", + &insecure_sym) != ENOENT); +} + static int link_elf_detect_insecure_early(struct thread *td, struct vnode *vp, const char *filename) @@ -209,18 +220,21 @@ link_elf_detect_insecure_early(struct thread *td, struct vnode *vp, static int link_elf_detect_insecure_late(struct thread *td, const char *filename, - linker_file_t lf) + struct vnode *vp, linker_file_t lf) { - c_linker_sym_t insecure_sym; + pax_flag_t flags; - if (link_elf_lookup_symbol(lf, "__insecure_kmod", &insecure_sym) != - ENOENT) { - if (!pax_insecure_kmod()) { - pax_log_internal(td->td_proc, PAX_LOG_P_COMM, - "Insecure kernel module load attempt rejected: %s", - filename != NULL ? filename : ""); - return (EPERM); - } + flags = pax_control_extattr_kmod(td, vp); + if ((flags & PAX_NOTE_PERMITKMOD) == PAX_NOTE_PERMITKMOD) { + return (0); + } + + if ((flags & PAX_NOTE_FORBIDKMOD) == PAX_NOTE_FORBIDKMOD || + (link_elf_kmod_is_insecure(lf) && !pax_insecure_kmod())) { + pax_log_internal(td->td_proc, PAX_LOG_P_COMM, + "Insecure kernel module load attempt rejected: %s", + filename != NULL ? filename : ""); + return (EPERM); } return (0); @@ -1240,7 +1254,7 @@ link_elf_load_file(linker_class_t cls, const char *filename, goto out; } - error = link_elf_detect_insecure_late(td, filename, lf); + error = link_elf_detect_insecure_late(td, filename, nd->ni_vp, lf); if (error != 0) { goto out; } diff --git a/sys/sys/linker.h b/sys/sys/linker.h index 2d65630cd66b..08e0bd4707cc 100644 --- a/sys/sys/linker.h +++ b/sys/sys/linker.h @@ -35,6 +35,7 @@ #include #include +#include #ifdef MALLOC_DECLARE MALLOC_DECLARE(M_LINKER); @@ -104,6 +105,7 @@ struct linker_file { caddr_t exidx_addr; /* Unwind data index table start */ size_t exidx_size; /* Unwind data index table size */ #endif + pax_flag_t pax_flags; }; /* diff --git a/sys/sys/pax.h b/sys/sys/pax.h index da27e5c51d80..cf259fdb500f 100644 --- a/sys/sys/pax.h +++ b/sys/sys/pax.h @@ -125,6 +125,7 @@ bool pax_feature_simple_validate_state(pax_state_t *state); */ int pax_control_acl_set_flags(struct thread *td, struct image_params *imgp, const pax_flag_t req_flags); int pax_control_extattr_parse_flags(struct thread *td, struct image_params *imgp); +pax_flag_t pax_control_extattr_kmod(struct thread *td, struct vnode *vp); /* * ASLR related functions @@ -241,17 +242,20 @@ bool pax_insecure_kmod(void); #define PAX_NOTE_NOSHLIBRANDOM 0x00000200 #define PAX_NOTE_DISALLOWMAP32BIT 0x00000400 #define PAX_NOTE_NODISALLOWMAP32BIT 0x00000800 +#define PAX_NOTE_PERMITKMOD 0x00001000 +#define PAX_NOTE_FORBIDKMOD 0x00002000 #define PAX_NOTE_RESERVED0 0x40000000 #define PAX_NOTE_PREFER_ACL 0x80000000 #define PAX_NOTE_ALL_ENABLED \ (PAX_NOTE_PAGEEXEC | PAX_NOTE_MPROTECT | PAX_NOTE_SEGVGUARD | \ - PAX_NOTE_ASLR | PAX_NOTE_SHLIBRANDOM | PAX_NOTE_DISALLOWMAP32BIT) + PAX_NOTE_ASLR | PAX_NOTE_SHLIBRANDOM | PAX_NOTE_DISALLOWMAP32BIT | \ + PAX_NOTE_PERMITKMOD) #define PAX_NOTE_ALL_DISABLED \ (PAX_NOTE_NOPAGEEXEC | PAX_NOTE_NOMPROTECT | \ PAX_NOTE_NOSEGVGUARD | PAX_NOTE_NOASLR | PAX_NOTE_NOSHLIBRANDOM | \ - PAX_NOTE_NODISALLOWMAP32BIT) + PAX_NOTE_NODISALLOWMAP32BIT | PAX_NOTE_FORBIDKMOD) #define PAX_NOTE_ALL (PAX_NOTE_ALL_ENABLED | PAX_NOTE_ALL_DISABLED | PAX_NOTE_PREFER_ACL) #define PAX_HARDENING_SHLIBRANDOM 0x00000100