X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=security%2Fsecurity.c;h=95d6b85a951c30c55b08fbd70fd6d86a487709da;hb=de5594ae40a34151754190e229f43535518564e7;hp=30132378d103dea6dc3d81ae65c2a04b864d71ec;hpb=0a6109fd1b605cbfa58ae9ae9d571d0f4140b727;p=mirror_ubuntu-artful-kernel.git diff --git a/security/security.c b/security/security.c index 30132378d103..95d6b85a951c 100644 --- a/security/security.c +++ b/security/security.c @@ -27,20 +27,45 @@ #include #include #include +#include #include #define MAX_LSM_EVM_XATTR 2 /* Maximum number of letters for an LSM name string */ #define SECURITY_NAME_MAX 10 +#define SECURITY_CHOSEN_NAMES_MAX (SECURITY_NAME_MAX * LSM_MAX_MAJOR) +#define MODULE_STACK "(stacking)" struct security_hook_heads security_hook_heads __lsm_ro_after_init; static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain); char *lsm_names; + +/* + * If stacking is enabled the task blob will always + * include an indicator of what security module data + * should be displayed. This is set with PR_SET_DISPLAY_LSM. + */ +static struct lsm_blob_sizes blob_sizes = { +#ifdef CONFIG_SECURITY_STACKING + .lbs_task = SECURITY_NAME_MAX + 6, +#endif +}; + /* Boot-time LSM user choice */ -static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = +static __initdata char chosen_lsms[SECURITY_CHOSEN_NAMES_MAX + 1] = +#ifdef CONFIG_SECURITY_STACKING + MODULE_STACK; +#else CONFIG_DEFAULT_SECURITY; +#endif +static __initdata char chosen_display_lsm[SECURITY_NAME_MAX + 1] +#ifdef CONFIG_SECURITY_STACKING + = CONFIG_SECURITY_DEFAULT_DISPLAY_NAME +#endif +; +static char default_display_lsm[SECURITY_NAME_MAX + 1]; static void __init do_security_initcalls(void) { @@ -75,21 +100,46 @@ int __init security_init(void) loadpin_add_hooks(); /* - * Load all the remaining security modules. + * The first call to a module specific init function + * updates the blob size requirements. */ do_security_initcalls(); + /* + * The second call to a module specific init function + * adds hooks to the hook lists and does any other early + * initializations required. + */ + do_security_initcalls(); + +#ifdef CONFIG_SECURITY_LSM_DEBUG + pr_info("LSM: cred blob size = %d\n", blob_sizes.lbs_cred); + pr_info("LSM: file blob size = %d\n", blob_sizes.lbs_file); + pr_info("LSM: task blob size = %d\n", blob_sizes.lbs_task); +#endif + return 0; } /* Save user chosen LSM */ static int __init choose_lsm(char *str) { - strncpy(chosen_lsm, str, SECURITY_NAME_MAX); + strncpy(chosen_lsms, str, SECURITY_CHOSEN_NAMES_MAX); + pr_info("LSM: command line set '%s' security module(s).\n", + chosen_lsms); return 1; } __setup("security=", choose_lsm); +static int __init choose_display_lsm(char *str) +{ + strncpy(chosen_display_lsm, str, SECURITY_NAME_MAX); + pr_info("LSM: command line set default display lsm %s'\n", + chosen_display_lsm); + return 1; +} +__setup("security.display=", choose_display_lsm); + static bool match_last_lsm(const char *list, const char *lsm) { const char *last; @@ -127,6 +177,7 @@ static int lsm_append(char *new, char **result) /** * security_module_enable - Load given security module on boot ? * @module: the name of the module + * @stacked: indicates that the module wants to be stacked * * Each LSM must pass this method before registering its own operations * to avoid security registration races. This method may also be used @@ -142,10 +193,71 @@ static int lsm_append(char *new, char **result) * * Otherwise, return false. */ -int __init security_module_enable(const char *module) +#ifdef CONFIG_SECURITY_STACKING +static bool __init cmp_lsms(const char *lsm) { - return !strcmp(module, chosen_lsm); + const char *str = chosen_lsms; + const char *split; + int len = strlen(lsm); + + if (len > SECURITY_NAME_MAX) { + pr_info("LSM: security module name '%s' exceeds limit\n", lsm); + return false; + } + for (split = strchr(str, ','); split; split = strchr(str, ',')) { + if ((len == split - str) && !strncmp(lsm, str, split - str)) + return true; + str = split + 1; + } + if ((len == strlen(str)) && !strncmp(lsm, str, strlen(str))) + return true; + return false; } +#endif + +bool __init security_module_enable(const char *lsm, const bool stacked) +{ +#ifdef CONFIG_SECURITY_STACKING + /* + * Module defined on the command line security=XXXX + */ + if (strcmp(chosen_lsms, MODULE_STACK)) { + if (cmp_lsms(lsm)) { + /* set to first LSM registered and then override */ + if (!*default_display_lsm) + strcpy(default_display_lsm, lsm); + else if (*chosen_display_lsm && !strcmp(chosen_display_lsm, lsm)) { + strcpy(default_display_lsm, lsm); + pr_info("LSM: default display lsm '%s'\n", default_display_lsm); + } + return true; + } + return false; + } + /* + * Module configured as stacked. + */ + if (stacked && !*default_display_lsm) + strcpy(default_display_lsm, lsm); + else if (stacked && *chosen_display_lsm && !strcmp(chosen_display_lsm, lsm)) { + strcpy(default_display_lsm, lsm); + pr_info("LSM: default display lsm '%s'\n", default_display_lsm); + } + + return stacked; +#else + if (strcmp(lsm, chosen_lsms) == 0) { + strcpy(default_display_lsm, lsm); + return true; + } + return false; +#endif +} + +/* + * Keep the order of major modules for mapping secids. + */ +static int lsm_next_major; /** * security_add_hooks - Add a modules hooks to the hook lists. @@ -159,9 +271,14 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count, char *lsm) { int i; + int lsm_index = lsm_next_major++; +#ifdef CONFIG_SECURITY_LSM_DEBUG + pr_info("LSM: Security module %s gets index %d\n", lsm, lsm_index); +#endif for (i = 0; i < count; i++) { hooks[i].lsm = lsm; + hooks[i].lsm_index = lsm_index; list_add_tail_rcu(&hooks[i].list, hooks[i].head); } if (lsm_append(lsm, &lsm_names) < 0) @@ -186,6 +303,189 @@ int unregister_lsm_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_lsm_notifier); +/** + * lsm_cred_alloc - allocate a composite cred blob + * @cred: the cred that needs a blob + * @gfp: allocation type + * + * Allocate the cred blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +int lsm_cred_alloc(struct cred *cred, gfp_t gfp) +{ +#ifdef CONFIG_SECURITY_LSM_DEBUG + if (cred->security) + pr_info("%s: Inbound cred blob is not NULL.\n", __func__); +#endif + if (blob_sizes.lbs_cred == 0) + return 0; + + cred->security = kzalloc(blob_sizes.lbs_cred, gfp); + if (cred->security == NULL) + return -ENOMEM; + return 0; +} + +/** + * lsm_early_cred - during initialization allocate a composite cred blob + * @cred: the cred that needs a blob + * + * Allocate the cred blob for all the modules if it's not already there + */ +void lsm_early_cred(struct cred *cred) +{ + int rc; + + if (cred == NULL) + panic("%s: NULL cred.\n", __func__); + if (cred->security != NULL) + return; + rc = lsm_cred_alloc(cred, GFP_KERNEL); + if (rc) + panic("%s: Early cred alloc failed.\n", __func__); +} + +static void __init lsm_set_size(int *need, int *lbs) +{ + int offset; + + if (*need > 0) { + offset = *lbs; + *lbs += *need; + *need = offset; + } +} + +/** + * security_add_blobs - Report blob sizes + * @needed: the size of blobs needed by the module + * + * Each LSM has to register its blobs with the infrastructure. + * The "needed" data tells the infrastructure how much memory + * the module requires for each of its blobs. On return the + * structure is filled with the offset that module should use + * from the blob pointer. + */ +void __init security_add_blobs(struct lsm_blob_sizes *needed) +{ + lsm_set_size(&needed->lbs_cred, &blob_sizes.lbs_cred); + lsm_set_size(&needed->lbs_file, &blob_sizes.lbs_file); + lsm_set_size(&needed->lbs_task, &blob_sizes.lbs_task); +} + +/** + * lsm_file_alloc - allocate a composite file blob + * @file: the file that needs a blob + * + * Allocate the file blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +int lsm_file_alloc(struct file *file) +{ +#ifdef CONFIG_SECURITY_LSM_DEBUG + if (file->f_security) + pr_info("%s: Inbound file blob is not NULL.\n", __func__); +#endif + if (blob_sizes.lbs_file == 0) + return 0; + + file->f_security = kzalloc(blob_sizes.lbs_file, GFP_KERNEL); + if (file->f_security == NULL) + return -ENOMEM; + return 0; +} + +#ifdef CONFIG_SECURITY_STACKING +static inline char *lsm_of_task(struct task_struct *task) +{ +#ifdef CONFIG_SECURITY_LSM_DEBUG + if (task->security == NULL) + pr_info("%s: task has no lsm name.\n", __func__); +#endif + return task->security; +} +#endif + +#ifdef CONFIG_SECURITY_STACKING +struct lsm_value { + char *lsm; + char *data; +}; + +/** + * lsm_parse_context - break a compound "context" into module data + * @cxt: the initial data, which will be modified + * @vlist: an array to receive the results + * + * Returns the number of entries, or -EINVAL if the cxt is unworkable. + */ +static int lsm_parse_context(char *cxt, struct lsm_value *vlist) +{ + char *lsm; + char *data; + char *cp; + int i; + + lsm = cxt; + for (i = 0; i < LSM_MAX_MAJOR; i++) { + data = strstr(lsm, "='"); + if (!data) + break; + *data = '\0'; + data += 2; + cp = strchr(data, '\''); + if (!cp) + return -EINVAL; + *cp++ = '\0'; + vlist[i].lsm = lsm; + vlist[i].data = data; + if (*cp == '\0') { + i++; + break; + } + if (*cp == ',') + cp++; + else + return -EINVAL; + lsm = cp; + } + return i; +} +#endif /* CONFIG_SECURITY_STACKING */ + +/** + * lsm_task_alloc - allocate a composite task blob + * @task: the task that needs a blob + * + * Allocate the task blob for all the modules + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +int lsm_task_alloc(struct task_struct *task) +{ +#ifdef CONFIG_SECURITY_LSM_DEBUG + if (task->security) + pr_info("%s: Inbound task blob is not NULL.\n", __func__); +#endif + if (blob_sizes.lbs_task == 0) + return 0; + + task->security = kzalloc(blob_sizes.lbs_task, GFP_KERNEL); + if (task->security == NULL) + return -ENOMEM; + + /* inherit current display lsm */ +#ifdef CONFIG_SECURITY_STACKING + if (current->security) + strcpy(task->security, lsm_of_task(current)); + else + strcpy(task->security, default_display_lsm); +#endif + return 0; +} + /* * Hook list operation macros. * @@ -535,6 +835,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry) return 0; return call_int_hook(path_rmdir, 0, dir, dentry); } +EXPORT_SYMBOL_GPL(security_path_rmdir); int security_path_unlink(const struct path *dir, struct dentry *dentry) { @@ -551,6 +852,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry, return 0; return call_int_hook(path_symlink, 0, dir, dentry, old_name); } +EXPORT_SYMBOL_GPL(security_path_symlink); int security_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) @@ -559,6 +861,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir, return 0; return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry); } +EXPORT_SYMBOL_GPL(security_path_link); int security_path_rename(const struct path *old_dir, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry, @@ -586,6 +889,7 @@ int security_path_truncate(const struct path *path) return 0; return call_int_hook(path_truncate, 0, path); } +EXPORT_SYMBOL_GPL(security_path_truncate); int security_path_chmod(const struct path *path, umode_t mode) { @@ -593,6 +897,7 @@ int security_path_chmod(const struct path *path, umode_t mode) return 0; return call_int_hook(path_chmod, 0, path, mode); } +EXPORT_SYMBOL_GPL(security_path_chmod); int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) { @@ -600,6 +905,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid) return 0; return call_int_hook(path_chown, 0, path, uid, gid); } +EXPORT_SYMBOL_GPL(security_path_chown); int security_path_chroot(const struct path *path) { @@ -685,6 +991,7 @@ int security_inode_readlink(struct dentry *dentry) return 0; return call_int_hook(inode_readlink, 0, dentry); } +EXPORT_SYMBOL_GPL(security_inode_readlink); int security_inode_follow_link(struct dentry *dentry, struct inode *inode, bool rcu) @@ -700,6 +1007,7 @@ int security_inode_permission(struct inode *inode, int mask) return 0; return call_int_hook(inode_permission, 0, inode, mask); } +EXPORT_SYMBOL_GPL(security_inode_permission); int security_inode_setattr(struct dentry *dentry, struct iattr *attr) { @@ -871,15 +1179,23 @@ int security_file_permission(struct file *file, int mask) return fsnotify_perm(file, mask); } +EXPORT_SYMBOL_GPL(security_file_permission); int security_file_alloc(struct file *file) { + int rc = lsm_file_alloc(file); + + if (rc) + return rc; return call_int_hook(file_alloc_security, 0, file); } void security_file_free(struct file *file) { call_void_hook(file_free_security, file); + + kfree(file->f_security); + file->f_security = NULL; } int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) @@ -930,6 +1246,7 @@ int security_mmap_file(struct file *file, unsigned long prot, return ret; return ima_file_mmap(file, prot); } +EXPORT_SYMBOL_GPL(security_mmap_file); int security_mmap_addr(unsigned long addr) { @@ -986,26 +1303,46 @@ int security_task_create(unsigned long clone_flags) int security_task_alloc(struct task_struct *task, unsigned long clone_flags) { + int rc = lsm_task_alloc(task); + + if (rc) + return rc; return call_int_hook(task_alloc, 0, task, clone_flags); } void security_task_free(struct task_struct *task) { call_void_hook(task_free, task); + + kfree(task->security); + task->security = NULL; } int security_cred_alloc_blank(struct cred *cred, gfp_t gfp) { + int rc = lsm_cred_alloc(cred, gfp); + + if (rc) + return rc; + return call_int_hook(cred_alloc_blank, 0, cred, gfp); } void security_cred_free(struct cred *cred) { call_void_hook(cred_free, cred); + + kfree(cred->security); + cred->security = NULL; } int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp) { + int rc = lsm_cred_alloc(new, gfp); + + if (rc) + return rc; + return call_int_hook(cred_prepare, 0, new, old, gfp); } @@ -1128,6 +1465,80 @@ int security_task_kill(struct task_struct *p, struct siginfo *info, return call_int_hook(task_kill, 0, p, info, sig, secid); } +#ifdef CONFIG_SECURITY_STACKING +static char *nolsm = "-default"; +#define NOLSMLEN 9 + +static bool is_registered_lsm(const char *str, size_t size) +{ + struct security_hook_list *hp; + + list_for_each_entry(hp, &security_hook_heads.getprocattr, list) { + if (size == strlen(hp->lsm) && !strncmp(str, hp->lsm, size)) + return true; + } + + return false; +} + +static bool set_lsm_of_current(const char *str, size_t size) +{ + char *lsm = lsm_of_task(current); + + if (is_registered_lsm(str, size)) { + strncpy(lsm, str, size); + lsm[size] = '\0'; + } else if (size == NOLSMLEN && !strncmp(str, nolsm, size)) { + lsm[0] = '\0'; + } else { + return false; + } + return true; +} + +static int lsm_task_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + char *lsm = lsm_of_task(current); + char buffer[SECURITY_NAME_MAX + 1]; + __user char *optval = (__user char *)arg2; + __user int *optlen = (__user int *)arg3; + int dlen; + int len; + + switch (option) { + case PR_GET_DISPLAY_LSM: + len = arg4; + if (lsm[0] == '\0') { + lsm = nolsm; + dlen = NOLSMLEN; + } else + dlen = strlen(lsm) + 1; + if (dlen > len) + return -ERANGE; + if (copy_to_user(optval, lsm, dlen)) + return -EFAULT; + if (put_user(dlen, optlen)) + return -EFAULT; + break; + case PR_SET_DISPLAY_LSM: + len = arg3; + if (len > SECURITY_NAME_MAX) + return -EINVAL; + if (copy_from_user(buffer, optval, len)) + return -EFAULT; + buffer[len] = '\0'; + /* verify the requested LSM is registered */ + if (!set_lsm_of_current(buffer, len)) + return -ENOENT; + break; + default: + return -ENOSYS; + } + return 0; +} +#endif + int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { @@ -1135,6 +1546,12 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, int rc = -ENOSYS; struct security_hook_list *hp; +#ifdef CONFIG_SECURITY_STACKING + rc = lsm_task_prctl(option, arg2, arg3, arg4, arg5); + if (rc != -ENOSYS) + return rc; +#endif + list_for_each_entry(hp, &security_hook_heads.task_prctl, list) { thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5); if (thisrc != -ENOSYS) { @@ -1263,14 +1680,184 @@ void security_d_instantiate(struct dentry *dentry, struct inode *inode) } EXPORT_SYMBOL(security_d_instantiate); -int security_getprocattr(struct task_struct *p, char *name, char **value) +int security_getprocattr(struct task_struct *p, const char *lsm, char *name, + char **value) { - return call_int_hook(getprocattr, -EINVAL, p, name, value); +#ifdef CONFIG_SECURITY_STACKING + char *speclsm = lsm_of_task(p); +#endif + struct security_hook_list *hp; + char *vp; + char *cp = NULL; + int trc; + int rc; + + /* + * "context" requires work here in addition to what + * the modules provide. + */ + if (strcmp(name, "context") == 0) { + *value = NULL; + rc = -EINVAL; + list_for_each_entry(hp, + &security_hook_heads.getprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; + trc = hp->hook.getprocattr(p, "context", &vp); + if (trc == -ENOENT) + continue; + if (trc <= 0) { + kfree(*value); + return trc; + } + rc = trc; + if (*value == NULL) { + *value = vp; + } else { + cp = kasprintf(GFP_KERNEL, "%s,%s", *value, vp); + if (cp == NULL) { + kfree(*value); + kfree(vp); + return -ENOMEM; + } + kfree(*value); + kfree(vp); + *value = cp; + } + } + if (rc > 0) + return strlen(*value); + return rc; + } else if (strcmp(name, "display_lsm") == 0) { + *value = kstrdup(current->security, GFP_KERNEL); + if (*value == NULL) + return -ENOMEM; + return strlen(*value); + } + + list_for_each_entry(hp, &security_hook_heads.getprocattr, list) { + if (lsm != NULL && strcmp(lsm, hp->lsm)) + continue; +#ifdef CONFIG_SECURITY_STACKING + if (!lsm && speclsm && speclsm[0] && strcmp(speclsm, hp->lsm)) + continue; +#endif + rc = hp->hook.getprocattr(p, name, value); + if (rc != -ENOSYS) + return rc; + } + return -EINVAL; } -int security_setprocattr(const char *name, void *value, size_t size) +int security_setprocattr(const char *lsm, const char *name, void *value, + size_t size) { - return call_int_hook(setprocattr, -EINVAL, name, value, size); +#ifdef CONFIG_SECURITY_STACKING + char *speclsm = lsm_of_task(current); + struct lsm_value *lsm_value = NULL; + int count; +#else + char *tvalue; +#endif + struct security_hook_list *hp; + int rc; + char *temp; + char *cp; + + if (!size) + return -EINVAL; + + /* + * If lsm is NULL look at all the modules to find one + * that processes name. If lsm is not NULL only look at + * that module. + * + * "context" is handled directly here. + */ + if (strcmp(name, "context") == 0) { + rc = -EINVAL; + temp = kmemdup(value, size + 1, GFP_KERNEL); + if (!temp) + return -ENOMEM; + + temp[size] = '\0'; + cp = strrchr(temp, '\''); + if (!cp) + goto free_out; + + cp[1] = '\0'; +#ifdef CONFIG_SECURITY_STACKING + lsm_value = kzalloc(sizeof(*lsm_value) * LSM_MAX_MAJOR, + GFP_KERNEL); + if (!lsm_value) { + rc = -ENOMEM; + goto free_out; + } + + count = lsm_parse_context(temp, lsm_value); + if (count <= 0) + goto free_out; + + for (count--; count >= 0; count--) { + list_for_each_entry(hp, + &security_hook_heads.setprocattr, list) { + + if (lsm && strcmp(lsm, hp->lsm)) + continue; + if (!strcmp(hp->lsm, lsm_value[count].lsm)) { + rc = hp->hook.setprocattr("context", + lsm_value[count].data, + strlen(lsm_value[count].data)); + break; + } + } + if (rc < 0 || (lsm && rc >0)) + break; + } +#else /* CONFIG_SECURITY_STACKING */ + cp = strstr(temp, "='"); + if (!cp) + goto free_out; + *cp = '\0'; + tvalue = strchr(cp + 2, '\''); + if (!tvalue) + goto free_out; + list_for_each_entry(hp, &security_hook_heads.setprocattr, + list) { + if (lsm == NULL || !strcmp(lsm, hp->lsm)) { + rc = hp->hook.setprocattr(name, tvalue, size); + break; + } + } +#endif /* CONFIG_SECURITY_STACKING */ +free_out: + kfree(temp); +#ifdef CONFIG_SECURITY_STACKING + kfree(lsm_value); +#endif + if (rc >= 0) + return size; + return rc; + } else if (strcmp(name, "display_lsm") == 0) { +#ifdef CONFIG_SECURITY_STACKING + if (set_lsm_of_current(value, size)) + return size; +#endif + return -EINVAL; + } + + list_for_each_entry(hp, &security_hook_heads.setprocattr, list) { + if (lsm && strcmp(lsm, hp->lsm)) + continue; +#ifdef CONFIG_SECURITY_STACKING + if (!lsm && speclsm && speclsm[0] && strcmp(speclsm, hp->lsm)) + continue; +#endif + rc = hp->hook.setprocattr(name, value, size); + if (rc) + return rc; + } + return -EINVAL; } int security_netlink_send(struct sock *sk, struct sk_buff *skb) @@ -1300,7 +1887,19 @@ EXPORT_SYMBOL(security_secctx_to_secid); void security_release_secctx(char *secdata, u32 seclen) { - call_void_hook(release_secctx, secdata, seclen); +#ifdef CONFIG_SECURITY_STACKING + char *speclsm = lsm_of_task(current); +#endif + struct security_hook_list *hp; + + list_for_each_entry(hp, &security_hook_heads.release_secctx, list) { +#ifdef CONFIG_SECURITY_STACKING + if (speclsm[0] && strcmp(hp->lsm, speclsm)) + continue; +#endif + hp->hook.release_secctx(secdata, seclen); + break; + } } EXPORT_SYMBOL(security_release_secctx); @@ -1419,8 +2018,21 @@ EXPORT_SYMBOL(security_sock_rcv_skb); int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, int __user *optlen, unsigned len) { +#ifdef CONFIG_SECURITY_STACKING + struct security_hook_list *hp; + char *lsm = lsm_of_task(current); + + list_for_each_entry(hp, &security_hook_heads.socket_getpeersec_stream, + list) { + if (!lsm || !lsm[0] || !strcmp(lsm, hp->lsm)) + return hp->hook.socket_getpeersec_stream(sock, optval, + optlen, len); + } + return -ENOPROTOOPT; +#else return call_int_hook(socket_getpeersec_stream, -ENOPROTOOPT, sock, optval, optlen, len); +#endif } int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid)