From da080a9effae4cca4ca6ba036f1b346770d39175 Mon Sep 17 00:00:00 2001 From: Bruce Ashfield Date: Wed, 3 Sep 2008 16:19:22 -0400 Subject: [PATCH 2/4] bsdjail: port BSD jail to 2.6.34 Update BSD jail to the 2.6.34 security environment. Some notable changes in the newer mode are: - no secondary registration - cannot unregister a secondary module - inode -> d_entry is required for inode_permissions - security is placed in task->cred, not in task. - jail_task_alloc_security jail_cred_prepare and jail_cred_commit - jail_task_free_security to jail_cred_free - ptrace_may_access to ptrace_access_check - jail_bprm_alloc_security to jail_bprm_set_creds - delete unused jail_proc_inode_permission Signed-off-by: Bruce Ashfield Signed-off-by: Liming Wang --- fs/proc/base.c | 4 +- include/linux/security.h | 6 +- security/bsdjail.c | 282 ++++++++++++++++++++++------------------------ security/commoncap.c | 2 + security/security.c | 10 ++ 5 files changed, 151 insertions(+), 153 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 31cd6dc..3690213 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2852,8 +2852,10 @@ retry: goto retry; } get_task_struct(iter.task); - if (security_task_lookup(task)) + if (security_task_lookup(iter.task)) { + iter.tgid += 1; goto retry; + } } rcu_read_unlock(); return iter; diff --git a/include/linux/security.h b/include/linux/security.h index f179007..e505739 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1915,6 +1915,7 @@ int security_netlink_recv(struct sk_buff *skb, int cap); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); void security_release_secctx(char *secdata, u32 seclen); +int security_task_lookup(struct task_struct *p); int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen); int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen); @@ -2534,11 +2535,6 @@ static inline int security_task_prctl(int option, unsigned long arg2, static inline int security_task_lookup(struct task_struct *p) { - return security_ops->task_lookup(p); -} - -static inline int security_task_lookup(struct task_struct *p) -{ return 0; } diff --git a/security/bsdjail.c b/security/bsdjail.c index 0d47889..2e2e13d 100644 --- a/security/bsdjail.c +++ b/security/bsdjail.c @@ -13,7 +13,6 @@ * (at your option) any later version. */ -#include #include #include #include @@ -21,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -39,8 +39,8 @@ static int jail_debug = 0; module_param(jail_debug, uint, 0600); MODULE_PARM_DESC(jail_debug, "Print bsd jail debugging messages.\n"); -#define DBG 0 -#define WARN 1 +#define BSDJAIL_DBG 0 +#define BSDJAIL_WARN 1 #define bsdj_debug(how, fmt, arg... ) \ do { \ if ( how || jail_debug ) \ @@ -51,9 +51,6 @@ MODULE_PARM_DESC(jail_debug, "Print bsd jail debugging messages.\n"); #define MY_NAME "bsdjail" -/* flag to keep track of how we were registered */ -static int secondary = 0; - /* * The task structure holding jail information. * Taskp->security points to one of these (or is null). @@ -76,7 +73,7 @@ struct jail_struct { int max_nrtask; /* maximum number of tasks within this jail. */ int cur_nrtask; /* current number of tasks within this jail. */ long maxtimeslice; /* max timeslice in ms for procs in this jail */ - long nice; /* nice level for processes in this jail */ + long nice; /* nice level for processes in this jail */ long max_data, max_memlock; /* equivalent to RLIMIT_{DATA,MEMLOCK} */ /* values for the jail_flags field */ #define GOT_NETWORK 1 /* if not set, jail can use any valid net address */ @@ -95,13 +92,13 @@ struct jail_struct { * structs, defines, and functions to cope with stacking */ -#define get_task_security(task) (task->security) +#define get_cred_security(cred) (cred->security) #define get_inode_security(inode) (inode->i_security) #define get_sock_security(sock) (sock->sk_security) #define get_file_security(file) (file->f_security) #define get_ipc_security(ipc) (ipc->security) -#define jail_of(proc) (get_task_security(proc)) +#define jail_of(proc) (get_cred_security(__task_cred(proc))) static void release_jail(struct kref *kref); @@ -135,35 +132,35 @@ static void free_jail(struct jail_struct *tsec) kfree(tsec); } -#define set_task_security(task,data) task->security = data +#define set_cred_security(cred, data) (cred->security = data) #define set_inode_security(inode,data) inode->i_security = data #define set_sock_security(sock,data) sock->sk_security = data #define set_file_security(file,data) file->f_security = data #define set_ipc_security(ipc,data) ipc.security = data /* - * jail_task_free_security: this is the callback hooked into LSM. - * If there was no task->security field for bsdjail, do nothing. + * jail_cred_free: this is the callback hooked into LSM. + * If there was no cred->security field for bsdjail, do nothing. * If there was, but it was never put into use, free the jail. * If there was, and the jail is in use, then decrement the usage * count, and disable and free the jail if the usage count hits 0. */ -static void jail_task_free_security(struct task_struct *task) +static void jail_cred_free(struct cred *cred) { struct jail_struct *tsec; - tsec = get_task_security(task); + tsec = get_cred_security(cred); if (!tsec) return; if (!in_use(tsec)) { - /* + /* * someone did 'echo -n x > /proc//attr/exec' but * then forked before execing. Nuke the old info. */ free_jail(tsec); - set_task_security(task,NULL); + set_cred_security(cred, NULL); return; } tsec->cur_nrtask--; @@ -175,11 +172,12 @@ static struct jail_struct * alloc_task_security(struct task_struct *tsk) { struct jail_struct *tsec; + struct cred *cred = (struct cred *)__task_cred(tsk); tsec = kmalloc(sizeof(struct jail_struct), GFP_KERNEL); if (!tsec) return ERR_PTR(-ENOMEM); memset(tsec, 0, sizeof(struct jail_struct)); - set_task_security(tsk, tsec); + set_cred_security(cred, tsec); return tsec; } @@ -187,7 +185,7 @@ static inline int in_jail(struct task_struct *t) { struct jail_struct *tsec = jail_of(t); - + if (tsec && in_use(tsec)) return 1; @@ -215,7 +213,7 @@ setup_netaddress(struct jail_struct *tsec) return; tsec->realaddr = htonl((a<<24)|(b<<16)|(c<<8)|d); set_got_network(tsec); - bsdj_debug(DBG, "Network set up (%s)\n", tsec->ip_addr_name); + bsdj_debug(BSDJAIL_DBG, "Network set up (%s)\n", tsec->ip_addr_name); } /* release_jail: @@ -225,7 +223,7 @@ setup_netaddress(struct jail_struct *tsec) static void release_jail(struct kref *kref) { struct jail_struct *tsec; - + tsec = container_of(kref,struct jail_struct,kref); disable_jail(tsec); free_jail(tsec); @@ -250,7 +248,7 @@ int enable_jail(struct task_struct *tsk) if (!tsec || !tsec->root_pathname) goto out; - /* + /* * USE_JAIL_NAMESPACE: could be useful, so that future mounts outside * the jail don't affect the jail. But it's not necessary, and * requires exporting copy_namespace from fs/namespace.c @@ -260,26 +258,26 @@ int enable_jail(struct task_struct *tsk) #define USE_JAIL_NAMESPACE */ #ifdef USE_JAIL_NAMESPACE - bsdj_debug(DBG, "bsdjail: copying namespace.\n"); + bsdj_debug(BSDJAIL_DBG, "bsdjail: copying namespace.\n"); retval = -EPERM; if (copy_namespaces(CLONE_NEWNS, tsk)) goto out; - bsdj_debug(DBG, "bsdjail: copied namespace.\n"); + bsdj_debug(BSDJAIL_DBG, "bsdjail: copied namespace.\n"); #endif /* find our new root directory */ - bsdj_debug(DBG, "bsdjail: looking up %s\n", tsec->root_pathname); + bsdj_debug(BSDJAIL_DBG, "bsdjail: looking up %s\n", tsec->root_pathname); retval = path_lookup(tsec->root_pathname, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); if (retval) goto out; - bsdj_debug(DBG, "bsdjail: got %s, setting root to it\n", tsec->root_pathname); + bsdj_debug(BSDJAIL_DBG, "bsdjail: got %s, setting root to it\n", tsec->root_pathname); /* and set the fsroot to it */ - set_fs_root(tsk->fs, nd.mnt, nd.dentry); - set_fs_pwd(tsk->fs, nd.mnt, nd.dentry); + set_fs_root(tsk->fs, &nd.path); + set_fs_pwd(tsk->fs, &nd.path); - bsdj_debug(DBG, "bsdjail: root has been set. Have fun.\n"); + bsdj_debug(BSDJAIL_DBG, "bsdjail: root has been set. Have fun.\n"); /* set up networking */ if (tsec->ip_addr_name) @@ -301,9 +299,9 @@ int enable_jail(struct task_struct *tsk) current->signal->rlim[RLIMIT_CPU].rlim_max = tsec->maxtimeslice; } /* success and end */ - tsec->mnt = mntget(nd.mnt); - tsec->dentry = dget(nd.dentry); - path_release(&nd); + tsec->mnt = mntget(nd.path.mnt); + tsec->dentry = dget(nd.path.dentry); + path_put(&nd.path); kref_init(&tsec->kref); set_in_use(tsec); @@ -333,7 +331,7 @@ jail_setprocattr(struct task_struct *p, char *name, void *value, size_t size) if (tsec && in_use(tsec)) return -EINVAL; /* let them guess why */ - + if (p != current || strcmp(name, "exec")) return -EPERM; @@ -456,23 +454,23 @@ static int print_jail_net_info(struct jail_struct *j, char *buf, int maxcnt) static int jail_getprocattr(struct task_struct *p, char *name, char **value) { - struct jail_struct *tsec; int err = 0; #if 0 // XXX + struct jail_struct *tsec; if (in_jail(current)) { if (strcmp(name, "current")==0) { /* provide network info */ - err = print_jail_net_info(jail_of(current), value, + err = print_jail_net_info(jail_of(current), *value, size); return err; } return -EINVAL; /* let them guess why */ } - + if (strcmp(name, "exec") == 0) { /* Print usage some help */ - err = snprintf(value, size, + err = snprintf(*value, size, "Valid keywords:\n" "root \n" "ip \n" @@ -489,9 +487,9 @@ jail_getprocattr(struct task_struct *p, char *name, char **value) tsec = jail_of(p); if (!tsec || !in_use(tsec)) { - err = snprintf(value, size, "Not Jailed\n"); + err = snprintf(*value, size, "Not Jailed\n"); } else { - err = snprintf(value, size, + err = snprintf(*value, size, "Root: %s\nIP: %s\n" "max_nrtask %d current nrtask %d max_timeslice %lu " "nice %lu\n" @@ -525,7 +523,7 @@ jail_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, if (!in_jail(current)) return 0; - file = (struct file *)((long)fown - offsetof(struct file,f_owner)); + file = (struct file *)((long)fown - offsetof(struct file,f_owner)); tsec = jail_of(tsk); fsec = get_file_security(file); @@ -581,17 +579,39 @@ static void free_inode_security(struct inode *inode) set_inode_security(inode, NULL); } -/* +/* + * LSM ptrace hook: + * process in jail may not ptrace process not in the same jail + */ +/* static int */ +/* jail_ptrace (struct task_struct *tracer, struct task_struct *tracee) */ +/* { */ +/* struct jail_struct *tsec = jail_of(tracer); */ + +/* if (tsec && (tsec->jail_flags & IN_USE)) { */ +/* if (tsec == tracee->security) */ +/* return 0; */ +/* return -EPERM; */ +/* } */ +/* return 0; */ +/* } */ + +/* * LSM ptrace hook: * process in jail may not ptrace process not in the same jail */ static int -jail_ptrace (struct task_struct *tracer, struct task_struct *tracee) +jail_ptrace_access_check(struct task_struct *ctp, unsigned int mode) { - struct jail_struct *tsec = jail_of(tracer); + struct jail_struct *tsec = ctp->cred->security; + int rc; + + rc = cap_ptrace_access_check(ctp, mode); + if (rc != 0) + return rc; if (tsec && in_use(tsec)) { - if (tsec == jail_of(tracee)) + if (tsec == jail_of(current)) return 0; return -EPERM; } @@ -639,9 +659,9 @@ jail_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) return 0; if (sin_addr == loopbackaddr || !sin_addr) { - bsdj_debug(DBG, "Got a loopback or 0 address\n"); + bsdj_debug(BSDJAIL_DBG, "Got a loopback or 0 address\n"); sin_addr = jailaddr; - bsdj_debug(DBG, "Converted to: %u.%u.%u.%u\n", + bsdj_debug(BSDJAIL_DBG, "Converted to: %u.%u.%u.%u\n", NIPQUAD(sin_addr)); return 0; } @@ -666,7 +686,7 @@ jail_socket_post_create(struct socket *sock, int family, int type, return err; // XXX inet = (struct inet_sock *)sock->sk; - inet->saddr = tsec->realaddr; + inet->inet_saddr = tsec->realaddr; return err; // XXX } @@ -688,7 +708,7 @@ jail_socket_listen(struct socket *sock, int backlog) inet = (struct inet_sock *)sock->sk; - if (inet->saddr == tsec->realaddr) + if (inet->inet_saddr == tsec->realaddr) return 0; return -EPERM; @@ -764,8 +784,8 @@ jail_socket_unix_stream_connect(struct socket *sock, } static int -jail_mount(char * dev_name, struct nameidata *nd, char * type, - unsigned long flags, void * data) +jail_mount(char * dev_name, struct path *path, char * type, + unsigned long flags, void * data) { if (in_jail(current)) return -EPERM; @@ -782,15 +802,22 @@ jail_umount(struct vfsmount *mnt, int flags) return 0; } -/* +/* * process in jail may not: * use nice * change network config * load/unload modules */ static int -jail_capable (struct task_struct *tsk, int cap) +jail_capable(struct task_struct *tsk, const struct cred *cred, + int cap, int audit) { + int rc; + + rc = cap_capable(tsk, cred, cap, audit); + if (rc) + return rc; + if (in_jail(tsk)) { if (cap == CAP_SYS_NICE) return -EPERM; @@ -802,7 +829,7 @@ jail_capable (struct task_struct *tsk, int cap) return -EPERM; } - if (cap_is_fs_cap (cap) ? tsk->fsuid == 0 : tsk->euid == 0) + if (cap_is_fs_cap(cap) ? tsk->cred->fsuid == 0 : tsk->cred->euid == 0) return 0; return -EPERM; } @@ -810,7 +837,7 @@ jail_capable (struct task_struct *tsk, int cap) /* * jail_security_task_create: * - * If the current process is ina a jail, and that jail is about to exceed a + * If the current process is in a jail, and that jail is about to exceed a * maximum number of processes, then refuse to fork. If the maximum number * of jails is listed as 0, then there is no limit for this jail, and we allow * all forks. @@ -828,20 +855,9 @@ jail_security_task_create (unsigned long clone_flags) return 0; } -/* - * The child of a process in a jail belongs in the same jail - */ -static int -jail_task_alloc_security(struct task_struct *tsk) +static void +jail_set_task_rlimit(struct task_struct *tsk, struct jail_struct *tsec) { - struct jail_struct *tsec = jail_of(current); - - if (!tsec || !in_use(tsec)) - return 0; - - set_task_security(tsk, tsec); - kref_get(&tsec->kref); - tsec->cur_nrtask++; if (tsec->maxtimeslice) { tsk->signal->rlim[RLIMIT_CPU].rlim_max = tsec->maxtimeslice; tsk->signal->rlim[RLIMIT_CPU].rlim_cur = tsec->maxtimeslice; @@ -856,12 +872,40 @@ jail_task_alloc_security(struct task_struct *tsk) } if (tsec->nice) set_user_nice(current, tsec->nice); +} + +static int +jail_cred_prepare(struct cred *new, const struct cred *old, + gfp_t gfp) +{ + struct jail_struct *tsec = get_cred_security(old); + + if (!tsec || !in_use(tsec)) + return 0; + + set_cred_security(new, tsec); + kref_get(&tsec->kref); + tsec->cur_nrtask++; + jail_set_task_rlimit(current, tsec); return 0; } +static void jail_cred_commit(struct cred *new, const struct cred *old) +{ + struct jail_struct *tsec = get_cred_security(old); + + if (!tsec || !in_use(tsec)) + return; + + set_cred_security(new, tsec); + kref_get(&tsec->kref); + tsec->cur_nrtask++; + jail_set_task_rlimit(current, tsec); +} + static int -jail_bprm_alloc_security(struct linux_binprm *bprm) +jail_bprm_set_creds(struct linux_binprm *bprm) { struct jail_struct *tsec; int ret; @@ -877,7 +921,7 @@ jail_bprm_alloc_security(struct linux_binprm *bprm) ret = enable_jail(current); if (ret) { /* if we failed, nix out the root/ip requests */ - jail_task_free_security(current); + jail_cred_free((struct cred *)__task_cred(current)); return ret; } } @@ -885,7 +929,7 @@ jail_bprm_alloc_security(struct linux_binprm *bprm) } /* - * Process in jail may not create devices + * Process in jail may not create devices * Thanks to Brad Spender for pointing out fifos should be allowed. */ /* TODO: We may want to allow /dev/log, at least... */ @@ -925,42 +969,6 @@ out: } /* - * jail_proc_inode_permission: - * called only when current is in a jail, and is trying to reach - * /proc/. We check whether is in the same jail as - * current. If not, permission is denied. - * - * NOTE: On the one hand, the task_to_inode(inode)->i_security - * approach seems cleaner, but on the other, this prevents us - * from unloading bsdjail for awhile... - */ -static int -jail_proc_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) -{ - struct jail_struct *tsec = jail_of(current); - struct dentry *dentry = nd->dentry; - unsigned pid; - - pid = name_to_int(dentry); - if (pid == ~0U) { - struct qstr *dname = &dentry->d_name; - if (strcmp(dname->name, "scsi")==0 || - strcmp(dname->name, "sys")==0 || - strcmp(dname->name, "ide")==0) - return -EPERM; - return 0; - } - - if (dentry->d_parent != dentry->d_sb->s_root) - return 0; - if (get_inode_security(inode) != tsec) - return -ENOENT; - - return 0; -} - -/* * Here is our attempt to prevent chroot escapes. */ static int @@ -1029,36 +1037,24 @@ static void jail_task_to_inode(struct task_struct *p, struct inode *inode) * permission is denied. */ static int -jail_inode_permission(struct inode *inode, int mask, - struct nameidata *nd) +jail_inode_permission(struct inode *inode, int mask ) { struct jail_struct *tsec = jail_of(current); - if (!tsec || !in_use(tsec)) return 0; - if (!nd) - return 0; - - if (nd->dentry && - strcmp(nd->dentry->d_sb->s_type->name, "proc")==0) { - return jail_proc_inode_permission(inode, mask, nd); - - } - if (!(mask&MAY_EXEC)) return 0; if (!inode || !S_ISDIR(inode->i_mode)) return 0; - if (is_jailroot_parent(nd->dentry, tsec->dentry, tsec->mnt)) { - bsdj_debug(WARN,"Attempt to chdir(..) out of jail!\n" + if (is_jailroot_parent(d_find_alias(inode), tsec->dentry, tsec->mnt)) { + bsdj_debug(BSDJAIL_WARN,"Attempt to chdir(..) out of jail!\n" "(%s is a subdir of %s)\n", tsec->dentry->d_name.name, - nd->dentry->d_name.name); + d_find_alias(inode)->d_name.name); return -EPERM; } - return 0; } @@ -1097,7 +1093,7 @@ jail_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) /* This probably is not necessary - /proc does not support xattrs? */ static int -jail_inode_getxattr(struct dentry *dentry, char *name) +jail_inode_getxattr(struct dentry *dentry, const char *name) { return generic_procpid_check(dentry); } @@ -1290,13 +1286,14 @@ jail_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, } static struct security_operations bsdjail_security_ops = { - .ptrace = jail_ptrace, + .ptrace_access_check = jail_ptrace_access_check, .capable = jail_capable, .task_kill = jail_task_kill, - .task_alloc_security = jail_task_alloc_security, - .task_free_security = jail_task_free_security, - .bprm_alloc_security = jail_bprm_alloc_security, + .cred_prepare = jail_cred_prepare, + .cred_commit = jail_cred_commit, + .cred_free = jail_cred_free, + .bprm_set_creds = jail_bprm_set_creds, .task_create = jail_security_task_create, .task_to_inode = jail_task_to_inode, .task_lookup = jail_task_lookup, @@ -1314,7 +1311,7 @@ static struct security_operations bsdjail_security_ops = { .socket_bind = jail_socket_bind, .socket_listen = jail_socket_listen, .socket_post_create = jail_socket_post_create, - .unix_stream_connect = jail_socket_unix_stream_connect, + .unix_stream_connect = jail_socket_unix_stream_connect, .unix_may_send = jail_socket_unix_may_send, .sk_free_security = free_sock_security, @@ -1349,19 +1346,10 @@ static struct security_operations bsdjail_security_ops = { static int __init bsdjail_init (void) { - int rc = 0; - if (register_security (&bsdjail_security_ops)) { - printk (KERN_INFO + printk (KERN_INFO "Failure registering BSD Jail module with the kernel\n"); - - rc = mod_reg_security(MY_NAME, &bsdjail_security_ops); - if (rc < 0) { - printk (KERN_INFO "Failure registering BSD Jail " - " module with primary security module.\n"); - return -EINVAL; - } - secondary = 1; + return -EINVAL; } printk (KERN_INFO "BSD Jail module initialized.\n"); @@ -1370,18 +1358,18 @@ static int __init bsdjail_init (void) static void __exit bsdjail_exit (void) { - if (secondary) { - if (mod_unreg_security (MY_NAME, &bsdjail_security_ops)) - printk (KERN_INFO "Failure unregistering BSD Jail " - " module with primary module.\n"); - } else { - if (unregister_security (&bsdjail_security_ops)) { - printk (KERN_INFO "Failure unregistering BSD Jail " - "module with the kernel\n"); - } +/* bva: in 2.6.24 the unregister options have been removed, they + either need to be reinstated or make the module a one way + process */ +#if 0 + if (unregister_security (&bsdjail_security_ops)) { + printk (KERN_INFO "Failure unregistering BSD Jail " + "module with the kernel\n"); + return; } printk (KERN_INFO "BSD Jail module removed\n"); +#endif } security_initcall (bsdjail_init); diff --git a/security/commoncap.c b/security/commoncap.c index 6166973..f749f83 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -85,6 +85,7 @@ int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, { return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; } +EXPORT_SYMBOL(cap_capable); /** * cap_settime - Determine whether the current process may set the system clock @@ -122,6 +123,7 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) rcu_read_unlock(); return ret; } +EXPORT_SYMBOL(cap_ptrace_access_check); /** * cap_ptrace_traceme - Determine whether another process may trace the current diff --git a/security/security.c b/security/security.c index a29f88d..89ffa6c 100644 --- a/security/security.c +++ b/security/security.c @@ -132,6 +132,7 @@ int register_security(struct security_operations *ops) return 0; } +EXPORT_SYMBOL_GPL(register_security); /* Security operations */ @@ -1043,6 +1044,15 @@ int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) } EXPORT_SYMBOL(security_inode_getsecctx); +int security_task_lookup(struct task_struct *p) +{ + if (security_ops && security_ops->task_lookup) + return security_ops->task_lookup(p); + + return 0; +} +EXPORT_SYMBOL(security_task_lookup); + #ifdef CONFIG_SECURITY_NETWORK int security_unix_stream_connect(struct socket *sock, struct socket *other, -- 1.6.5.2