scm.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:6k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /* scm.c - Socket level control messages processing.
  2.  *
  3.  * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  4.  *              Alignment and value checking mods by Craig Metz
  5.  *
  6.  * This program is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU General Public License
  8.  * as published by the Free Software Foundation; either version
  9.  * 2 of the License, or (at your option) any later version.
  10.  */
  11. #include <linux/signal.h>
  12. #include <linux/errno.h>
  13. #include <linux/sched.h>
  14. #include <linux/mm.h>
  15. #include <linux/kernel.h>
  16. #include <linux/major.h>
  17. #include <linux/stat.h>
  18. #include <linux/socket.h>
  19. #include <linux/file.h>
  20. #include <linux/fcntl.h>
  21. #include <linux/net.h>
  22. #include <linux/interrupt.h>
  23. #include <linux/netdevice.h>
  24. #include <asm/system.h>
  25. #include <asm/uaccess.h>
  26. #include <net/protocol.h>
  27. #include <linux/skbuff.h>
  28. #include <net/sock.h>
  29. #include <net/scm.h>
  30. /*
  31.  * Only allow a user to send credentials, that they could set with 
  32.  * setu(g)id.
  33.  */
  34. static __inline__ int scm_check_creds(struct ucred *creds)
  35. {
  36. if ((creds->pid == current->pid || capable(CAP_SYS_ADMIN)) &&
  37.     ((creds->uid == current->uid || creds->uid == current->euid ||
  38.       creds->uid == current->suid) || capable(CAP_SETUID)) &&
  39.     ((creds->gid == current->gid || creds->gid == current->egid ||
  40.       creds->gid == current->sgid) || capable(CAP_SETGID))) {
  41.        return 0;
  42. }
  43. return -EPERM;
  44. }
  45. static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
  46. {
  47. int *fdp = (int*)CMSG_DATA(cmsg);
  48. struct scm_fp_list *fpl = *fplp;
  49. struct file **fpp;
  50. int i, num;
  51. num = (cmsg->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr)))/sizeof(int);
  52. if (num <= 0)
  53. return 0;
  54. if (num > SCM_MAX_FD)
  55. return -EINVAL;
  56. if (!fpl)
  57. {
  58. fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
  59. if (!fpl)
  60. return -ENOMEM;
  61. *fplp = fpl;
  62. fpl->count = 0;
  63. }
  64. fpp = &fpl->fp[fpl->count];
  65. if (fpl->count + num > SCM_MAX_FD)
  66. return -EINVAL;
  67. /*
  68.  * Verify the descriptors and increment the usage count.
  69.  */
  70.  
  71. for (i=0; i< num; i++)
  72. {
  73. int fd = fdp[i];
  74. struct file *file;
  75. if (fd < 0 || !(file = fget(fd)))
  76. return -EBADF;
  77. *fpp++ = file;
  78. fpl->count++;
  79. }
  80. return num;
  81. }
  82. void __scm_destroy(struct scm_cookie *scm)
  83. {
  84. struct scm_fp_list *fpl = scm->fp;
  85. int i;
  86. if (fpl) {
  87. scm->fp = NULL;
  88. for (i=fpl->count-1; i>=0; i--)
  89. fput(fpl->fp[i]);
  90. kfree(fpl);
  91. }
  92. }
  93. int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
  94. {
  95. struct cmsghdr *cmsg;
  96. int err;
  97. for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
  98. {
  99. err = -EINVAL;
  100. /* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
  101. /* The first check was omitted in <= 2.2.5. The reasoning was
  102.    that parser checks cmsg_len in any case, so that
  103.    additional check would be work duplication.
  104.    But if cmsg_level is not SOL_SOCKET, we do not check 
  105.    for too short ancillary data object at all! Oops.
  106.    OK, let's add it...
  107.  */
  108. if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
  109.     (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
  110.     + cmsg->cmsg_len) > msg->msg_controllen)
  111. goto error;
  112. if (cmsg->cmsg_level != SOL_SOCKET)
  113. continue;
  114. switch (cmsg->cmsg_type)
  115. {
  116. case SCM_RIGHTS:
  117. err=scm_fp_copy(cmsg, &p->fp);
  118. if (err<0)
  119. goto error;
  120. break;
  121. case SCM_CREDENTIALS:
  122. if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
  123. goto error;
  124. memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred));
  125. err = scm_check_creds(&p->creds);
  126. if (err)
  127. goto error;
  128. break;
  129. default:
  130. goto error;
  131. }
  132. }
  133. if (p->fp && !p->fp->count)
  134. {
  135. kfree(p->fp);
  136. p->fp = NULL;
  137. }
  138. return 0;
  139. error:
  140. scm_destroy(p);
  141. return err;
  142. }
  143. int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
  144. {
  145. struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
  146. struct cmsghdr cmhdr;
  147. int cmlen = CMSG_LEN(len);
  148. int err;
  149. if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
  150. msg->msg_flags |= MSG_CTRUNC;
  151. return 0; /* XXX: return error? check spec. */
  152. }
  153. if (msg->msg_controllen < cmlen) {
  154. msg->msg_flags |= MSG_CTRUNC;
  155. cmlen = msg->msg_controllen;
  156. }
  157. cmhdr.cmsg_level = level;
  158. cmhdr.cmsg_type = type;
  159. cmhdr.cmsg_len = cmlen;
  160. err = -EFAULT;
  161. if (copy_to_user(cm, &cmhdr, sizeof cmhdr))
  162. goto out; 
  163. if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr)))
  164. goto out;
  165. cmlen = CMSG_SPACE(len);
  166. msg->msg_control += cmlen;
  167. msg->msg_controllen -= cmlen;
  168. err = 0;
  169. out:
  170. return err;
  171. }
  172. void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
  173. {
  174. struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
  175. int fdmax = 0;
  176. int fdnum = scm->fp->count;
  177. struct file **fp = scm->fp->fp;
  178. int *cmfptr;
  179. int err = 0, i;
  180. if (msg->msg_controllen > sizeof(struct cmsghdr))
  181. fdmax = ((msg->msg_controllen - sizeof(struct cmsghdr))
  182.  / sizeof(int));
  183. if (fdnum < fdmax)
  184. fdmax = fdnum;
  185. for (i=0, cmfptr=(int*)CMSG_DATA(cm); i<fdmax; i++, cmfptr++)
  186. {
  187. int new_fd;
  188. err = get_unused_fd();
  189. if (err < 0)
  190. break;
  191. new_fd = err;
  192. err = put_user(new_fd, cmfptr);
  193. if (err) {
  194. put_unused_fd(new_fd);
  195. break;
  196. }
  197. /* Bump the usage count and install the file. */
  198. get_file(fp[i]);
  199. fd_install(new_fd, fp[i]);
  200. }
  201. if (i > 0)
  202. {
  203. int cmlen = CMSG_LEN(i*sizeof(int));
  204. if (!err)
  205. err = put_user(SOL_SOCKET, &cm->cmsg_level);
  206. if (!err)
  207. err = put_user(SCM_RIGHTS, &cm->cmsg_type);
  208. if (!err)
  209. err = put_user(cmlen, &cm->cmsg_len);
  210. if (!err) {
  211. cmlen = CMSG_SPACE(i*sizeof(int));
  212. msg->msg_control += cmlen;
  213. msg->msg_controllen -= cmlen;
  214. }
  215. }
  216. if (i < fdnum || (fdnum && fdmax <= 0))
  217. msg->msg_flags |= MSG_CTRUNC;
  218. /*
  219.  * All of the files that fit in the message have had their
  220.  * usage counts incremented, so we just free the list.
  221.  */
  222. __scm_destroy(scm);
  223. }
  224. struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
  225. {
  226. struct scm_fp_list *new_fpl;
  227. int i;
  228. if (!fpl)
  229. return NULL;
  230. new_fpl = kmalloc(sizeof(*fpl), GFP_KERNEL);
  231. if (new_fpl) {
  232. for (i=fpl->count-1; i>=0; i--)
  233. get_file(fpl->fp[i]);
  234. memcpy(new_fpl, fpl, sizeof(*fpl));
  235. }
  236. return new_fpl;
  237. }