1 /* Copyright 2005 Princeton University
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above
11 copyright notice, this list of conditions and the following
12 disclaimer in the documentation and/or other materials provided
13 with the distribution.
15 * Neither the name of the copyright holder nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
23 UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29 WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/resource.h>
39 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <arpa/inet.h>
47 #include <sys/mount.h>
53 static inline PyObject *inc_and_ret_none(void)
59 #define NONE inc_and_ret_none()
61 #define PL_INSECURE_BCAPS (vc_get_insecurebcaps() | (1 << VC_CAP_NET_BIND_SERVICE))
62 #define PL_INSECURE_CCAPS vc_get_insecureccaps()
66 __vserver_get_rlimit(xid_t xid, int resource) {
67 struct vc_rlimit limits;
71 if (vc_get_rlimit(xid, resource, &limits)==-1)
72 ret = PyErr_SetFromErrno(PyExc_OSError);
74 ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
80 vserver_get_rlimit(PyObject *self, PyObject *args) {
85 if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
88 ret = __vserver_get_rlimit(xid, resource);
94 vserver_set_rlimit(PyObject *self, PyObject *args) {
95 struct vc_rlimit limits;
96 struct vc_rlimit_mask mask;
100 PyObject *ret = NULL;
102 limits.min = VC_LIM_KEEP;
103 limits.soft = VC_LIM_KEEP;
104 limits.hard = VC_LIM_KEEP;
106 if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
111 if (vc_get_rlimit_mask(xid, &mask)==-1) {
112 ret = PyErr_SetFromErrno(PyExc_OSError);
114 bitmask = (1<<resource);
115 if ((mask.min|mask.soft|mask.hard) & bitmask)
116 if (vc_set_rlimit(xid, resource, &limits)==-1)
117 ret = PyErr_SetFromErrno(PyExc_OSError);
119 ret = __vserver_get_rlimit(xid, resource);
127 vserver_get_dlimit(PyObject *self, PyObject *args)
132 struct vc_ctx_dlimit data;
135 if (!PyArg_ParseTuple(args, "si", &path,&xid))
138 memset(&data, 0, sizeof(data));
139 r = vc_get_dlimit(path, xid, 0, &data);
141 res = Py_BuildValue("(i,i,i,i,i)",
148 res = PyErr_SetFromErrno(PyExc_OSError);
156 vserver_set_dlimit(PyObject *self, PyObject *args)
160 struct vc_ctx_dlimit data;
162 memset(&data,0,sizeof(data));
163 if (!PyArg_ParseTuple(args, "siiiiii", &path,
172 if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
173 vc_set_dlimit(path, xid, 0, &data))
174 return PyErr_SetFromErrno(PyExc_OSError);
180 vserver_unset_dlimit(PyObject *self, PyObject *args)
185 if (!PyArg_ParseTuple(args, "si", &path, &xid))
188 if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
189 return PyErr_SetFromErrno(PyExc_OSError);
196 vserver_set_bcaps(PyObject *self, PyObject *args)
199 struct vc_ctx_caps caps;
201 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
204 caps.bmask = PL_INSECURE_BCAPS;
205 caps.cmask = caps.ccaps = 0;
206 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
207 return PyErr_SetFromErrno(PyExc_OSError);
213 vserver_text2bcaps(PyObject *self, PyObject *args)
215 struct vc_ctx_caps caps = { .bcaps = 0 };
218 struct vc_err_listparser err;
220 if (!PyArg_ParseTuple(args, "s#", &list, &len))
223 vc_list2bcap(list, len, &err, &caps);
225 return Py_BuildValue("K", caps.bcaps);
229 vserver_get_bcaps(PyObject *self, PyObject *args)
232 struct vc_ctx_caps caps;
234 if (!PyArg_ParseTuple(args, "I", &ctx))
237 if (vc_get_ccaps(ctx, &caps) == -1) {
239 return PyErr_SetFromErrno(PyExc_OSError);
244 return Py_BuildValue("K", caps.bcaps & PL_INSECURE_BCAPS);
248 vserver_bcaps2text(PyObject *self, PyObject *args)
250 struct vc_ctx_caps caps = { .bcaps = 0 };
254 if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
257 list = PyString_FromString("");
259 while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
262 PyString_ConcatAndDel(&list, PyString_FromFormat(
263 (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
271 vserver_set_ccaps(PyObject *self, PyObject *args)
274 struct vc_ctx_caps caps;
276 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.ccaps))
279 caps.cmask = PL_INSECURE_CCAPS;
280 caps.bmask = caps.bcaps = 0;
281 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
282 return PyErr_SetFromErrno(PyExc_OSError);
288 vserver_text2ccaps(PyObject *self, PyObject *args)
290 struct vc_ctx_caps caps = { .ccaps = 0 };
293 struct vc_err_listparser err;
295 if (!PyArg_ParseTuple(args, "s#", &list, &len))
298 vc_list2ccap(list, len, &err, &caps);
300 return Py_BuildValue("K", caps.ccaps);
304 vserver_get_ccaps(PyObject *self, PyObject *args)
307 struct vc_ctx_caps caps;
309 if (!PyArg_ParseTuple(args, "I", &ctx))
312 if (vc_get_ccaps(ctx, &caps) == -1) {
314 return PyErr_SetFromErrno(PyExc_OSError);
319 return Py_BuildValue("K", caps.ccaps & PL_INSECURE_CCAPS);
323 vserver_ccaps2text(PyObject *self, PyObject *args)
325 struct vc_ctx_caps caps = { .ccaps = 0 };
329 if (!PyArg_ParseTuple(args, "K", &caps.ccaps))
332 list = PyString_FromString("");
334 while ((cap = vc_loccap2text(&caps.ccaps)) != NULL) {
337 PyString_ConcatAndDel(&list, PyString_FromFormat(
338 (PyString_Size(list) > 0 ? ",%s" : "%s" ),
346 convert_address(const char *str, struct vc_net_addr *addr)
349 if (inet_pton(AF_INET6, str, addr->vna_v6_ip.s6_addr) > 0) {
350 addr->vna_type = VC_NXA_TYPE_IPV6;
353 else if (inet_pton(AF_INET, str, &addr->vna_v4_ip.s_addr) > 0) {
354 addr->vna_type = VC_NXA_TYPE_IPV4;
361 mask_to_prefix(void *data, int limit)
363 uint8_t *mask = data;
365 for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
371 get_mask(struct vc_net_addr *addr)
373 struct ifaddrs *head, *ifa;
375 int family, offset, len;
378 switch (addr->vna_type) {
379 case VC_NXA_TYPE_IPV4:
381 offset = offsetof(struct sockaddr_in, sin_addr.s_addr);
382 ip = &addr->vna_v4_ip.s_addr;
384 addr->vna_v4_mask.s_addr = htonl(0xffffff00);
385 addr->vna_prefix = 24;
387 case VC_NXA_TYPE_IPV6:
389 offset = offsetof(struct sockaddr_in6, sin6_addr.s6_addr);
390 ip = addr->vna_v6_ip.s6_addr;
392 addr->vna_v6_mask.s6_addr32[9] = addr->vna_v6_mask.s6_addr32[1] = 0xffffffff;
393 addr->vna_v6_mask.s6_addr32[2] = addr->vna_v6_mask.s6_addr32[3] = 0x00000000;
394 addr->vna_prefix = 64;
401 if (getifaddrs(&head) == -1)
403 for (ifa = head; ifa; ifa = ifa->ifa_next) {
404 if (ifa->ifa_addr && ifa->ifa_addr->sa_family == family &&
405 memcmp((char *) ifa->ifa_addr + offset, ip, len) == 0) {
406 switch (addr->vna_type) {
407 case VC_NXA_TYPE_IPV4:
408 memcpy(&addr->vna_v4_mask.s_addr, ifa->ifa_netmask + offset, len);
409 addr->vna_prefix = mask_to_prefix(&addr->vna_v4_mask.s_addr, 32);
411 case VC_NXA_TYPE_IPV6:
412 memcpy(addr->vna_v6_mask.s6_addr, ifa->ifa_netmask + offset, len);
413 addr->vna_prefix = mask_to_prefix(addr->vna_v6_mask.s6_addr, 128);
424 /* XXX These two functions are really similar */
426 vserver_net_add(PyObject *self, PyObject *args)
428 struct vc_net_addr addr = { .vna_type = 0 };
432 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
435 /* Optimize standard case, which also needs to be handled differently */
436 if (strcmp(ip, "0.0.0.0") == 0) {
437 addr.vna_type = VC_NXA_TYPE_MASK | VC_NXA_TYPE_IPV4;
441 addr.vna_v4_mask.s_addr = 0;
442 addr.vna_v4_ip.s_addr = 0;
445 if (convert_address(ip, &addr) == -1)
446 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
448 switch (get_mask(&addr)) {
450 return PyErr_SetFromErrno(PyExc_OSError);
452 /* XXX error here? */
455 addr.vna_type |= VC_NXA_TYPE_ADDR;
458 if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
459 return PyErr_SetFromErrno(PyExc_OSError);
465 vserver_net_remove(PyObject *self, PyObject *args)
467 struct vc_net_addr addr;
471 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
474 if (strcmp(ip, "all") == 0)
475 addr.vna_type = VC_NXA_TYPE_ANY;
476 else if (strcmp(ip, "all4") == 0)
477 addr.vna_type = VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_ANY;
478 else if (strcmp(ip, "all6") == 0)
479 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
481 if (convert_address(ip, &addr) == -1)
482 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
484 switch (get_mask(&addr)) {
486 return PyErr_SetFromErrno(PyExc_OSError);
489 addr.vna_type |= VC_NXA_TYPE_ADDR;
492 if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
493 return PyErr_SetFromErrno(PyExc_OSError);
508 if (fchdir(fd) == -1 || chroot(".") == -1)
514 restore_dirs(struct secure_dirs *dirs)
516 if (dirs->host_fd != -1) {
517 if (fchroot(dirs->host_fd) == -1)
519 if (close(dirs->host_fd) == -1)
522 if (dirs->guest_fd != -1) {
523 if (close(dirs->guest_fd) == -1)
526 if (dirs->target_fd != -1) {
527 if (close(dirs->target_fd) == -1)
530 if (dirs->cwd_fd != -1) {
531 if (fchdir(dirs->cwd_fd) == -1)
533 if (close(dirs->cwd_fd) == -1)
540 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
542 dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
544 dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
545 if (dirs->host_fd == -1)
548 dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
549 if (dirs->cwd_fd == -1)
552 dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
553 if (dirs->guest_fd == -1)
555 if (fchroot(dirs->guest_fd) == -1)
558 dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
559 if (dirs->target_fd == -1)
562 if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
565 if (close(dirs->guest_fd) == -1)
569 if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
576 vserver_mount(PyObject *self, PyObject *args)
578 const char *guest, *target, *source, *type, *data = NULL;
579 unsigned long flags = 0;
580 struct secure_dirs dirs;
582 if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
586 if (secure_chdir(&dirs, guest, target) == -1)
588 if (mount(source, ".", type, flags, data) == -1 && errno != EBUSY)
596 return PyErr_SetFromErrno(PyExc_OSError);
600 vserver_umount(PyObject *self, PyObject *args)
602 const char *guest, *target;
607 if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
610 path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
611 sprintf(path, "%s/%s", guest, target);
612 if (umount2(path, flags) == -1)
613 ret = PyErr_SetFromErrno(PyExc_OSError);
622 vserver_set_runlevel(PyObject *self, PyObject *args)
628 if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
633 memset(&ut, 0, sizeof(ut));
634 ut.ut_type = RUN_LVL;
635 ut.ut_pid = ('#' << 8) + runlevel + '0';
643 vserver_set_name(PyObject *self, PyObject *args)
648 if (!PyArg_ParseTuple(args, "II", &ctx, &slice_id))
651 if (vc_set_vhi_name(ctx, vcVHI_CONTEXT, (char *)&slice_id, sizeof(slice_id)) != 0 && errno != ESRCH) {
652 return PyErr_SetFromErrno(PyExc_OSError);
659 vserver_get_name(PyObject *self, PyObject *args)
664 if (!PyArg_ParseTuple(args, "I", &ctx))
667 if (vc_get_vhi_name(ctx, vcVHI_CONTEXT, (char *)&slice_id, sizeof(slice_id)) != 0) {
668 ret = PyErr_SetFromErrno(PyExc_OSError);
670 ret = Py_BuildValue("i", slice_id);
676 static PyMethodDef methods[] = {
677 { "setdlimit", vserver_set_dlimit, METH_VARARGS,
678 "Set disk limits for given vserver context" },
679 { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
680 "Remove disk limits for given vserver context" },
681 { "getdlimit", vserver_get_dlimit, METH_VARARGS,
682 "Get disk limits for given vserver context" },
683 { "setrlimit", vserver_set_rlimit, METH_VARARGS,
684 "Set resource limits for given resource of a vserver context" },
685 { "getrlimit", vserver_get_rlimit, METH_VARARGS,
686 "Get resource limits for given resource of a vserver context" },
687 { "setbcaps", vserver_set_bcaps, METH_VARARGS,
688 "Set POSIX capabilities of a vserver context" },
689 { "getbcaps", vserver_get_bcaps, METH_VARARGS,
690 "Get POSIX capabilities of a vserver context" },
691 { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
692 "Translate a string of capabilities to a bitmap" },
693 { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
694 "Translate a capability-bitmap into a string" },
695 { "setccaps", vserver_set_ccaps, METH_VARARGS,
696 "Set context capabilities of a vserver context" },
697 { "getccaps", vserver_get_ccaps, METH_VARARGS,
698 "Get context capabilities of a vserver context" },
699 { "text2ccaps", vserver_text2ccaps, METH_VARARGS,
700 "Translate a string of context capabilities to a bitmap" },
701 { "ccaps2text", vserver_ccaps2text, METH_VARARGS,
702 "Translate a context-capability-bitmap into a string" },
703 { "netadd", vserver_net_add, METH_VARARGS,
704 "Assign an IP address to a context" },
705 { "netremove", vserver_net_remove, METH_VARARGS,
706 "Remove IP address(es) from a context" },
707 { "mount", vserver_mount, METH_VARARGS,
708 "Perform the mount() system call" },
709 { "umount", vserver_umount, METH_VARARGS,
710 "Perform the umount2() system call" },
711 { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
712 "Set the runlevel in utmp" },
713 { "setname", vserver_set_name, METH_VARARGS,
714 "Set the vcVHI_CONTEXT for a xid." },
715 { "getname", vserver_get_name, METH_VARARGS,
716 "Get the vcVHI_CONTEXT for a xid." },
717 { NULL, NULL, 0, NULL }
721 initvserverimpl(void)
725 mod = Py_InitModule("vserverimpl", methods);
727 /* export the set of 'safe' capabilities */
728 PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
730 /* export the default vserver directory */
731 PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
733 /* export limit-related constants */
734 PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
735 PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
736 PyModule_AddIntConstant(mod, "VC_LIM_INFINITY", (int)VC_LIM_INFINITY);
737 PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
739 PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
740 PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
741 PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
742 PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
743 PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
744 PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
745 PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
747 PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
748 PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
750 PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
751 PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
752 PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
753 PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);