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 },
 
 |