41 * are currently assigned to their zone. Door clients in a zone can only
42 * modify links that were created in their zone.
43 *
44 * The datalink ID space is global, while each zone has its own datalink name
45 * space. This allows each zone to have complete freedom over the names that
46 * they assign to links created within the zone.
47 */
48
49 #include <assert.h>
50 #include <alloca.h>
51 #include <errno.h>
52 #include <priv_utils.h>
53 #include <stdlib.h>
54 #include <strings.h>
55 #include <syslog.h>
56 #include <sys/sysevent/eventdefs.h>
57 #include <zone.h>
58 #include <libsysevent.h>
59 #include <libdlmgmt.h>
60 #include <librcm.h>
61 #include "dlmgmt_impl.h"
62
63 typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
64 ucred_t *);
65
66 typedef struct dlmgmt_door_info_s {
67 uint_t di_cmd;
68 size_t di_reqsz;
69 size_t di_acksz;
70 dlmgmt_door_handler_t *di_handler;
71 } dlmgmt_door_info_t;
72
73 /*
74 * Check if the caller has the required privileges to operate on a link of the
75 * given class.
76 */
77 static int
78 dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred)
79 {
80 const priv_set_t *eset;
81
82 eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
362 datalink_id_t linkid = destroy->ld_linkid;
363 dlmgmt_link_t *linkp = NULL;
364 uint32_t flags, dflags = 0;
365 int err = 0;
366
367 flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0);
368
369 /*
370 * Hold the writer lock to update the link table.
371 */
372 dlmgmt_table_lock(B_TRUE);
373
374 if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
375 err = ENOENT;
376 goto done;
377 }
378
379 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
380 goto done;
381
382 if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) {
383 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0)
384 goto done;
385 dflags |= DLMGMT_ACTIVE;
386 }
387
388 if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) {
389 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0)
390 goto done;
391 dflags |= DLMGMT_PERSIST;
392 }
393
394 err = dlmgmt_destroy_common(linkp, flags);
395 done:
396 if (err != 0 && dflags != 0)
397 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags);
398
399 dlmgmt_table_unlock();
400 retvalp->lr_err = err;
401 }
631 boolean_t renamed = B_FALSE;
632 int err = 0;
633
634 if (!dladm_valid_linkname(remapid->ld_link)) {
635 retvalp->lr_err = EINVAL;
636 return;
637 }
638
639 /*
640 * Hold the writer lock to update the link table.
641 */
642 dlmgmt_table_lock(B_TRUE);
643 if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) {
644 err = ENOENT;
645 goto done;
646 }
647
648 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
649 goto done;
650
651 if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
652 err = EEXIST;
653 goto done;
654 }
655
656 (void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN);
657 avl_remove(&dlmgmt_name_avl, linkp);
658 (void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN);
659 avl_add(&dlmgmt_name_avl, linkp);
660 renamed = B_TRUE;
661
662 if (linkp->ll_flags & DLMGMT_ACTIVE) {
663 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE);
664 if (err != 0)
665 goto done;
666 }
667 if (linkp->ll_flags & DLMGMT_PERSIST) {
668 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST);
669 if (err != 0) {
670 if (linkp->ll_flags & DLMGMT_ACTIVE) {
692 dlmgmt_upid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
693 ucred_t *cred)
694 {
695 dlmgmt_door_upid_t *upid = argp;
696 dlmgmt_upid_retval_t *retvalp = retp;
697 dlmgmt_link_t *linkp;
698 int err = 0;
699
700 /*
701 * Hold the writer lock to update the link table.
702 */
703 dlmgmt_table_lock(B_TRUE);
704 if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) {
705 err = ENOENT;
706 goto done;
707 }
708
709 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
710 goto done;
711
712 if (linkp->ll_flags & DLMGMT_ACTIVE) {
713 err = EINVAL;
714 goto done;
715 }
716
717 if ((err = link_activate(linkp)) == 0) {
718 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
719 DLMGMT_ACTIVE);
720 }
721 done:
722 dlmgmt_table_unlock();
723 retvalp->lr_err = err;
724 }
725
726 /* ARGSUSED */
727 static void
728 dlmgmt_createconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
729 ucred_t *cred)
730 {
731 dlmgmt_door_createconf_t *createconf = argp;
1199 datalink_id_t linkid = setzoneid->ld_linkid;
1200 zoneid_t oldzoneid, newzoneid;
1201 int err = 0;
1202
1203 dlmgmt_table_lock(B_TRUE);
1204
1205 /* We currently only allow changing zoneid's from the global zone. */
1206 if (zoneid != GLOBAL_ZONEID) {
1207 err = EACCES;
1208 goto done;
1209 }
1210
1211 if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
1212 err = ENOENT;
1213 goto done;
1214 }
1215
1216 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
1217 goto done;
1218
1219 /* We can only assign an active link to a zone. */
1220 if (!(linkp->ll_flags & DLMGMT_ACTIVE)) {
1221 err = EINVAL;
1222 goto done;
1223 }
1224
1225 oldzoneid = linkp->ll_zoneid;
1226 newzoneid = setzoneid->ld_zoneid;
1227
1228 if (oldzoneid == newzoneid)
1229 goto done;
1230
1231 /*
1232 * Before we remove the link from its current zone, make sure that
1233 * there isn't a link with the same name in the destination zone.
1234 */
1235 if (zoneid != GLOBAL_ZONEID &&
1236 link_by_name(linkp->ll_link, newzoneid) != NULL) {
1237 err = EEXIST;
1238 goto done;
1292 goto done;
1293 }
1294
1295 if ((err = dlmgmt_elevate_privileges()) == 0) {
1296 err = dlmgmt_zone_init(zoneboot->ld_zoneid);
1297 (void) dlmgmt_drop_privileges();
1298 }
1299 done:
1300 dlmgmt_table_unlock();
1301 retvalp->lr_err = err;
1302 }
1303
1304 /* ARGSUSED */
1305 static void
1306 dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1307 ucred_t *cred)
1308 {
1309 int err = 0;
1310 dlmgmt_door_zonehalt_t *zonehalt = argp;
1311 dlmgmt_zonehalt_retval_t *retvalp = retp;
1312
1313 if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
1314 if (zoneid != GLOBAL_ZONEID) {
1315 err = EACCES;
1316 } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) {
1317 err = EINVAL;
1318 } else {
1319 dlmgmt_table_lock(B_TRUE);
1320 dlmgmt_db_fini(zonehalt->ld_zoneid);
1321 dlmgmt_table_unlock();
1322 }
1323 }
1324 retvalp->lr_err = err;
1325 }
1326
1327 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = {
1328 { DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t),
1329 sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create },
1330 { DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t),
1331 sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr },
1332 { DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t),
1333 sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy },
1334 { DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t),
1335 sizeof (dlmgmt_getname_retval_t), dlmgmt_getname },
1336 { DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t),
1337 sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid },
1338 { DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t),
1339 sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext },
1340 { DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t),
1341 sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update },
|
41 * are currently assigned to their zone. Door clients in a zone can only
42 * modify links that were created in their zone.
43 *
44 * The datalink ID space is global, while each zone has its own datalink name
45 * space. This allows each zone to have complete freedom over the names that
46 * they assign to links created within the zone.
47 */
48
49 #include <assert.h>
50 #include <alloca.h>
51 #include <errno.h>
52 #include <priv_utils.h>
53 #include <stdlib.h>
54 #include <strings.h>
55 #include <syslog.h>
56 #include <sys/sysevent/eventdefs.h>
57 #include <zone.h>
58 #include <libsysevent.h>
59 #include <libdlmgmt.h>
60 #include <librcm.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <fcntl.h>
64 #include <unistd.h>
65 #include "dlmgmt_impl.h"
66
67
68 typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
69 ucred_t *);
70
71 typedef struct dlmgmt_door_info_s {
72 uint_t di_cmd;
73 size_t di_reqsz;
74 size_t di_acksz;
75 dlmgmt_door_handler_t *di_handler;
76 } dlmgmt_door_info_t;
77
78 /*
79 * Check if the caller has the required privileges to operate on a link of the
80 * given class.
81 */
82 static int
83 dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred)
84 {
85 const priv_set_t *eset;
86
87 eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
367 datalink_id_t linkid = destroy->ld_linkid;
368 dlmgmt_link_t *linkp = NULL;
369 uint32_t flags, dflags = 0;
370 int err = 0;
371
372 flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0);
373
374 /*
375 * Hold the writer lock to update the link table.
376 */
377 dlmgmt_table_lock(B_TRUE);
378
379 if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
380 err = ENOENT;
381 goto done;
382 }
383
384 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
385 goto done;
386
387 if (linkp->ll_tomb == B_TRUE) {
388 err = EINPROGRESS;
389 goto done;
390 }
391
392 if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) {
393 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0)
394 goto done;
395 dflags |= DLMGMT_ACTIVE;
396 }
397
398 if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) {
399 if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0)
400 goto done;
401 dflags |= DLMGMT_PERSIST;
402 }
403
404 err = dlmgmt_destroy_common(linkp, flags);
405 done:
406 if (err != 0 && dflags != 0)
407 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags);
408
409 dlmgmt_table_unlock();
410 retvalp->lr_err = err;
411 }
641 boolean_t renamed = B_FALSE;
642 int err = 0;
643
644 if (!dladm_valid_linkname(remapid->ld_link)) {
645 retvalp->lr_err = EINVAL;
646 return;
647 }
648
649 /*
650 * Hold the writer lock to update the link table.
651 */
652 dlmgmt_table_lock(B_TRUE);
653 if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) {
654 err = ENOENT;
655 goto done;
656 }
657
658 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
659 goto done;
660
661 if (linkp->ll_tomb == B_TRUE) {
662 err = EBUSY;
663 goto done;
664 }
665
666
667 if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
668 err = EEXIST;
669 goto done;
670 }
671
672 (void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN);
673 avl_remove(&dlmgmt_name_avl, linkp);
674 (void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN);
675 avl_add(&dlmgmt_name_avl, linkp);
676 renamed = B_TRUE;
677
678 if (linkp->ll_flags & DLMGMT_ACTIVE) {
679 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE);
680 if (err != 0)
681 goto done;
682 }
683 if (linkp->ll_flags & DLMGMT_PERSIST) {
684 err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST);
685 if (err != 0) {
686 if (linkp->ll_flags & DLMGMT_ACTIVE) {
708 dlmgmt_upid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
709 ucred_t *cred)
710 {
711 dlmgmt_door_upid_t *upid = argp;
712 dlmgmt_upid_retval_t *retvalp = retp;
713 dlmgmt_link_t *linkp;
714 int err = 0;
715
716 /*
717 * Hold the writer lock to update the link table.
718 */
719 dlmgmt_table_lock(B_TRUE);
720 if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) {
721 err = ENOENT;
722 goto done;
723 }
724
725 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
726 goto done;
727
728 if (linkp->ll_tomb == B_TRUE) {
729 err = EBUSY;
730 goto done;
731 }
732
733 if (linkp->ll_flags & DLMGMT_ACTIVE) {
734 err = EINVAL;
735 goto done;
736 }
737
738 if ((err = link_activate(linkp)) == 0) {
739 (void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
740 DLMGMT_ACTIVE);
741 }
742 done:
743 dlmgmt_table_unlock();
744 retvalp->lr_err = err;
745 }
746
747 /* ARGSUSED */
748 static void
749 dlmgmt_createconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
750 ucred_t *cred)
751 {
752 dlmgmt_door_createconf_t *createconf = argp;
1220 datalink_id_t linkid = setzoneid->ld_linkid;
1221 zoneid_t oldzoneid, newzoneid;
1222 int err = 0;
1223
1224 dlmgmt_table_lock(B_TRUE);
1225
1226 /* We currently only allow changing zoneid's from the global zone. */
1227 if (zoneid != GLOBAL_ZONEID) {
1228 err = EACCES;
1229 goto done;
1230 }
1231
1232 if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
1233 err = ENOENT;
1234 goto done;
1235 }
1236
1237 if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
1238 goto done;
1239
1240 if (linkp->ll_tomb == B_TRUE) {
1241 err = EBUSY;
1242 goto done;
1243 }
1244
1245 /* We can only assign an active link to a zone. */
1246 if (!(linkp->ll_flags & DLMGMT_ACTIVE)) {
1247 err = EINVAL;
1248 goto done;
1249 }
1250
1251 oldzoneid = linkp->ll_zoneid;
1252 newzoneid = setzoneid->ld_zoneid;
1253
1254 if (oldzoneid == newzoneid)
1255 goto done;
1256
1257 /*
1258 * Before we remove the link from its current zone, make sure that
1259 * there isn't a link with the same name in the destination zone.
1260 */
1261 if (zoneid != GLOBAL_ZONEID &&
1262 link_by_name(linkp->ll_link, newzoneid) != NULL) {
1263 err = EEXIST;
1264 goto done;
1318 goto done;
1319 }
1320
1321 if ((err = dlmgmt_elevate_privileges()) == 0) {
1322 err = dlmgmt_zone_init(zoneboot->ld_zoneid);
1323 (void) dlmgmt_drop_privileges();
1324 }
1325 done:
1326 dlmgmt_table_unlock();
1327 retvalp->lr_err = err;
1328 }
1329
1330 /* ARGSUSED */
1331 static void
1332 dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1333 ucred_t *cred)
1334 {
1335 int err = 0;
1336 dlmgmt_door_zonehalt_t *zonehalt = argp;
1337 dlmgmt_zonehalt_retval_t *retvalp = retp;
1338 static char my_pid[10];
1339
1340 if (my_pid[0] == NULL)
1341 (void) snprintf(my_pid, sizeof (my_pid), "%d\n", getpid());
1342
1343 if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
1344 if (zoneid != GLOBAL_ZONEID) {
1345 err = EACCES;
1346 } else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) {
1347 err = EINVAL;
1348 } else {
1349 /*
1350 * dls and mac don't honor the locking rules defined in
1351 * mac. In order to try and make that case less likely
1352 * to happen, we try to serialize some of the zone
1353 * activity here between dlmgmtd and the brands on
1354 * /etc/dladm/zone.lck
1355 */
1356 int fd;
1357
1358 while ((fd = open(ZONE_LOCK, O_WRONLY |
1359 O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
1360 (void) sleep(1);
1361 (void) write(fd, my_pid, sizeof (my_pid));
1362 (void) close(fd);
1363
1364 dlmgmt_table_lock(B_TRUE);
1365 dlmgmt_db_fini(zonehalt->ld_zoneid);
1366 dlmgmt_table_unlock();
1367
1368 (void) unlink(ZONE_LOCK);
1369 }
1370 }
1371 retvalp->lr_err = err;
1372 }
1373
1374 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = {
1375 { DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t),
1376 sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create },
1377 { DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t),
1378 sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr },
1379 { DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t),
1380 sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy },
1381 { DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t),
1382 sizeof (dlmgmt_getname_retval_t), dlmgmt_getname },
1383 { DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t),
1384 sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid },
1385 { DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t),
1386 sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext },
1387 { DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t),
1388 sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update },
|