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>
52 #include "planetlab.h"
54 static inline PyObject *inc_and_ret_none(void)
60 #define NONE inc_and_ret_none()
62 #define PL_INSECURE_BCAPS (vc_get_insecurebcaps() | (1 << VC_CAP_NET_BIND_SERVICE))
63 #define PL_INSECURE_CCAPS vc_get_insecureccaps()
67 __vserver_get_rlimit(xid_t xid, int resource) {
68 struct vc_rlimit limits;
72 if (vc_get_rlimit(xid, resource, &limits)==-1)
73 ret = PyErr_SetFromErrno(PyExc_OSError);
75 ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
81 vserver_get_rlimit(PyObject *self, PyObject *args) {
86 if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
89 ret = __vserver_get_rlimit(xid, resource);
95 vserver_set_rlimit(PyObject *self, PyObject *args) {
96 struct vc_rlimit limits;
97 struct vc_rlimit_mask mask;
101 PyObject *ret = NULL;
103 limits.min = VC_LIM_KEEP;
104 limits.soft = VC_LIM_KEEP;
105 limits.hard = VC_LIM_KEEP;
107 if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
112 if (vc_get_rlimit_mask(xid, &mask)==-1) {
113 ret = PyErr_SetFromErrno(PyExc_OSError);
115 bitmask = (1<<resource);
116 if ((mask.min|mask.soft|mask.hard) & bitmask)
117 if (vc_set_rlimit(xid, resource, &limits)==-1)
118 ret = PyErr_SetFromErrno(PyExc_OSError);
120 ret = __vserver_get_rlimit(xid, resource);
128 vserver_get_dlimit(PyObject *self, PyObject *args)
133 struct vc_ctx_dlimit data;
136 if (!PyArg_ParseTuple(args, "si", &path,&xid))
139 memset(&data, 0, sizeof(data));
140 r = vc_get_dlimit(path, xid, 0, &data);
142 res = Py_BuildValue("(i,i,i,i,i)",
149 res = PyErr_SetFromErrno(PyExc_OSError);
157 vserver_set_dlimit(PyObject *self, PyObject *args)
161 struct vc_ctx_dlimit data;
163 memset(&data,0,sizeof(data));
164 if (!PyArg_ParseTuple(args, "siiiiii", &path,
173 if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
174 vc_set_dlimit(path, xid, 0, &data))
175 return PyErr_SetFromErrno(PyExc_OSError);
181 vserver_unset_dlimit(PyObject *self, PyObject *args)
186 if (!PyArg_ParseTuple(args, "si", &path, &xid))
189 if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
190 return PyErr_SetFromErrno(PyExc_OSError);
197 vserver_set_bcaps(PyObject *self, PyObject *args)
200 struct vc_ctx_caps caps;
202 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
205 caps.bmask = PL_INSECURE_BCAPS;
206 caps.cmask = caps.ccaps = 0;
207 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
208 return PyErr_SetFromErrno(PyExc_OSError);
214 vserver_text2bcaps(PyObject *self, PyObject *args)
216 struct vc_ctx_caps caps = { .bcaps = 0 };
219 struct vc_err_listparser err;
221 if (!PyArg_ParseTuple(args, "s#", &list, &len))
224 vc_list2bcap(list, len, &err, &caps);
226 return Py_BuildValue("K", caps.bcaps);
230 vserver_get_bcaps(PyObject *self, PyObject *args)
233 struct vc_ctx_caps caps;
235 if (!PyArg_ParseTuple(args, "I", &ctx))
238 if (vc_get_ccaps(ctx, &caps) == -1) {
240 return PyErr_SetFromErrno(PyExc_OSError);
245 return Py_BuildValue("K", caps.bcaps & PL_INSECURE_BCAPS);
249 vserver_bcaps2text(PyObject *self, PyObject *args)
251 struct vc_ctx_caps caps = { .bcaps = 0 };
255 if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
258 list = PyString_FromString("");
260 while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
263 PyString_ConcatAndDel(&list, PyString_FromFormat(
264 (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
272 vserver_set_ccaps(PyObject *self, PyObject *args)
275 struct vc_ctx_caps caps;
277 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.ccaps))
280 caps.cmask = PL_INSECURE_CCAPS;
281 caps.bmask = caps.bcaps = 0;
282 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
283 return PyErr_SetFromErrno(PyExc_OSError);
289 vserver_text2ccaps(PyObject *self, PyObject *args)
291 struct vc_ctx_caps caps = { .ccaps = 0 };
294 struct vc_err_listparser err;
296 if (!PyArg_ParseTuple(args, "s#", &list, &len))
299 vc_list2ccap(list, len, &err, &caps);
301 return Py_BuildValue("K", caps.ccaps);
305 vserver_get_ccaps(PyObject *self, PyObject *args)
308 struct vc_ctx_caps caps;
310 if (!PyArg_ParseTuple(args, "I", &ctx))
313 if (vc_get_ccaps(ctx, &caps) == -1) {
315 return PyErr_SetFromErrno(PyExc_OSError);
320 return Py_BuildValue("K", caps.ccaps & PL_INSECURE_CCAPS);
324 vserver_ccaps2text(PyObject *self, PyObject *args)
326 struct vc_ctx_caps caps = { .ccaps = 0 };
330 if (!PyArg_ParseTuple(args, "K", &caps.ccaps))
333 list = PyString_FromString("");
335 while ((cap = vc_loccap2text(&caps.ccaps)) != NULL) {
338 PyString_ConcatAndDel(&list, PyString_FromFormat(
339 (PyString_Size(list) > 0 ? ",%s" : "%s" ),
347 convert_address(const char *str, struct vc_net_addr *addr)
350 if (inet_pton(AF_INET6, str, addr->vna_v6_ip.s6_addr) > 0) {
351 addr->vna_type = VC_NXA_TYPE_IPV6;
354 else if (inet_pton(AF_INET, str, &addr->vna_v4_ip.s_addr) > 0) {
355 addr->vna_type = VC_NXA_TYPE_IPV4;
362 mask_to_prefix(void *data, int limit)
364 uint8_t *mask = data;
366 for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
372 get_mask(struct vc_net_addr *addr)
374 struct ifaddrs *head, *ifa;
376 int family, offset, len;
379 switch (addr->vna_type) {
380 case VC_NXA_TYPE_IPV4:
382 offset = offsetof(struct sockaddr_in, sin_addr.s_addr);
383 ip = &addr->vna_v4_ip.s_addr;
385 addr->vna_v4_mask.s_addr = htonl(0xffffff00);
386 addr->vna_prefix = 24;
388 case VC_NXA_TYPE_IPV6:
390 offset = offsetof(struct sockaddr_in6, sin6_addr.s6_addr);
391 ip = addr->vna_v6_ip.s6_addr;
393 addr->vna_v6_mask.s6_addr32[9] = addr->vna_v6_mask.s6_addr32[1] = 0xffffffff;
394 addr->vna_v6_mask.s6_addr32[2] = addr->vna_v6_mask.s6_addr32[3] = 0x00000000;
395 addr->vna_prefix = 64;
402 if (getifaddrs(&head) == -1)
404 for (ifa = head; ifa; ifa = ifa->ifa_next) {
405 if (ifa->ifa_addr->sa_family == family &&
406 memcmp((char *) ifa->ifa_addr + offset, ip, len) == 0) {
407 switch (addr->vna_type) {
408 case VC_NXA_TYPE_IPV4:
409 memcpy(&addr->vna_v4_mask.s_addr, ifa->ifa_netmask + offset, len);
410 addr->vna_prefix = mask_to_prefix(&addr->vna_v4_mask.s_addr, 32);
412 case VC_NXA_TYPE_IPV6:
413 memcpy(addr->vna_v6_mask.s6_addr, ifa->ifa_netmask + offset, len);
414 addr->vna_prefix = mask_to_prefix(addr->vna_v6_mask.s6_addr, 128);
425 /* XXX These two functions are really similar */
427 vserver_net_add(PyObject *self, PyObject *args)
429 struct vc_net_addr addr = { .vna_type = 0 };
433 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
436 /* Optimize standard case, which also needs to be handled differently */
437 if (strcmp(ip, "0.0.0.0") == 0) {
438 addr.vna_type = VC_NXA_TYPE_MASK | VC_NXA_TYPE_IPV4;
442 addr.vna_v4_mask.s_addr = 0;
443 addr.vna_v4_ip.s_addr = 0;
446 if (convert_address(ip, &addr) == -1)
447 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
449 switch (get_mask(&addr)) {
451 return PyErr_SetFromErrno(PyExc_OSError);
453 /* XXX error here? */
456 addr.vna_type |= VC_NXA_TYPE_ADDR;
459 if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
460 return PyErr_SetFromErrno(PyExc_OSError);
466 vserver_net_remove(PyObject *self, PyObject *args)
468 struct vc_net_addr addr;
472 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
475 if (strcmp(ip, "all") == 0)
476 addr.vna_type = VC_NXA_TYPE_ANY;
477 else if (strcmp(ip, "all4") == 0)
478 addr.vna_type = VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_ANY;
479 else if (strcmp(ip, "all6") == 0)
480 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
482 if (convert_address(ip, &addr) == -1)
483 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
485 switch (get_mask(&addr)) {
487 return PyErr_SetFromErrno(PyExc_OSError);
490 addr.vna_type |= VC_NXA_TYPE_ADDR;
493 if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
494 return PyErr_SetFromErrno(PyExc_OSError);
509 if (fchdir(fd) == -1 || chroot(".") == -1)
515 restore_dirs(struct secure_dirs *dirs)
517 if (dirs->host_fd != -1) {
518 if (fchroot(dirs->host_fd) == -1)
520 if (close(dirs->host_fd) == -1)
523 if (dirs->guest_fd != -1) {
524 if (close(dirs->guest_fd) == -1)
527 if (dirs->target_fd != -1) {
528 if (close(dirs->target_fd) == -1)
531 if (dirs->cwd_fd != -1) {
532 if (fchdir(dirs->cwd_fd) == -1)
534 if (close(dirs->cwd_fd) == -1)
541 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
543 dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
545 dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
546 if (dirs->host_fd == -1)
549 dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
550 if (dirs->cwd_fd == -1)
553 dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
554 if (dirs->guest_fd == -1)
556 if (fchroot(dirs->guest_fd) == -1)
559 dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
560 if (dirs->target_fd == -1)
563 if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
566 if (close(dirs->guest_fd) == -1)
570 if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
577 vserver_mount(PyObject *self, PyObject *args)
579 const char *guest, *target, *source, *type, *data = NULL;
580 unsigned long flags = 0;
581 struct secure_dirs dirs;
583 if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
587 if (secure_chdir(&dirs, guest, target) == -1)
589 if (mount(source, ".", type, flags, data) == -1 && errno != EBUSY)
597 return PyErr_SetFromErrno(PyExc_OSError);
601 vserver_umount(PyObject *self, PyObject *args)
603 const char *guest, *target;
608 if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
611 path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
612 sprintf(path, "%s/%s", guest, target);
613 if (umount2(path, flags) == -1)
614 ret = PyErr_SetFromErrno(PyExc_OSError);
623 vserver_set_runlevel(PyObject *self, PyObject *args)
629 if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
634 memset(&ut, 0, sizeof(ut));
635 ut.ut_type = RUN_LVL;
636 ut.ut_pid = ('#' << 8) + runlevel + '0';
644 vserver_set_name(PyObject *self, PyObject *args)
649 if (!PyArg_ParseTuple(args, "II", &ctx, &slice_id))
652 if (vc_set_vhi_name(ctx, vcVHI_CONTEXT, (char *)&slice_id, sizeof(slice_id)) != 0 && errno != ESRCH) {
653 return PyErr_SetFromErrno(PyExc_OSError);
660 vserver_get_name(PyObject *self, PyObject *args)
665 if (!PyArg_ParseTuple(args, "I", &ctx))
668 if (vc_get_vhi_name(ctx, vcVHI_CONTEXT, (char *)&slice_id, sizeof(slice_id)) != 0) {
669 ret = PyErr_SetFromErrno(PyExc_OSError);
671 ret = Py_BuildValue("i", slice_id);
677 static PyMethodDef methods[] = {
678 { "setdlimit", vserver_set_dlimit, METH_VARARGS,
679 "Set disk limits for given vserver context" },
680 { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
681 "Remove disk limits for given vserver context" },
682 { "getdlimit", vserver_get_dlimit, METH_VARARGS,
683 "Get disk limits for given vserver context" },
684 { "setrlimit", vserver_set_rlimit, METH_VARARGS,
685 "Set resource limits for given resource of a vserver context" },
686 { "getrlimit", vserver_get_rlimit, METH_VARARGS,
687 "Get resource limits for given resource of a vserver context" },
688 { "setbcaps", vserver_set_bcaps, METH_VARARGS,
689 "Set POSIX capabilities of a vserver context" },
690 { "getbcaps", vserver_get_bcaps, METH_VARARGS,
691 "Get POSIX capabilities of a vserver context" },
692 { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
693 "Translate a string of capabilities to a bitmap" },
694 { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
695 "Translate a capability-bitmap into a string" },
696 { "setccaps", vserver_set_ccaps, METH_VARARGS,
697 "Set context capabilities of a vserver context" },
698 { "getccaps", vserver_get_ccaps, METH_VARARGS,
699 "Get context capabilities of a vserver context" },
700 { "text2ccaps", vserver_text2ccaps, METH_VARARGS,
701 "Translate a string of context capabilities to a bitmap" },
702 { "ccaps2text", vserver_ccaps2text, METH_VARARGS,
703 "Translate a context-capability-bitmap into a string" },
704 { "netadd", vserver_net_add, METH_VARARGS,
705 "Assign an IP address to a context" },
706 { "netremove", vserver_net_remove, METH_VARARGS,
707 "Remove IP address(es) from a context" },
708 { "mount", vserver_mount, METH_VARARGS,
709 "Perform the mount() system call" },
710 { "umount", vserver_umount, METH_VARARGS,
711 "Perform the umount2() system call" },
712 { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
713 "Set the runlevel in utmp" },
714 { "setname", vserver_set_name, METH_VARARGS,
715 "Set the vcVHI_CONTEXT for a xid." },
716 { "getname", vserver_get_name, METH_VARARGS,
717 "Get the vcVHI_CONTEXT for a xid." },
718 { NULL, NULL, 0, NULL }
722 initvserverimpl(void)
726 mod = Py_InitModule("vserverimpl", methods);
728 /* export the set of 'safe' capabilities */
729 PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
731 /* export the default vserver directory */
732 PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
734 /* export limit-related constants */
735 PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
736 PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_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);