usr/src/cmd/dlmgmtd/dlmgmt_main.c
changeset 10616 3be00c4a6835
parent 9107 a47dcdb19715
child 11537 8eca52188202
equal deleted inserted replaced
10615:4bb212e117c7 10616:3be00c4a6835
    39  */
    39  */
    40 
    40 
    41 #include <assert.h>
    41 #include <assert.h>
    42 #include <errno.h>
    42 #include <errno.h>
    43 #include <fcntl.h>
    43 #include <fcntl.h>
    44 #include <priv_utils.h>
    44 #include <priv.h>
    45 #include <signal.h>
    45 #include <signal.h>
    46 #include <stdlib.h>
    46 #include <stdlib.h>
    47 #include <stdio.h>
    47 #include <stdio.h>
    48 #include <strings.h>
    48 #include <strings.h>
    49 #include <syslog.h>
    49 #include <syslog.h>
       
    50 #include <zone.h>
    50 #include <sys/dld.h>
    51 #include <sys/dld.h>
    51 #include <sys/dld_ioc.h>
    52 #include <sys/dld_ioc.h>
    52 #include <sys/param.h>
    53 #include <sys/param.h>
    53 #include <sys/stat.h>
    54 #include <sys/stat.h>
    54 #include <unistd.h>
    55 #include <unistd.h>
    64  * handle because the door isn't created when the handle is created.
    65  * handle because the door isn't created when the handle is created.
    65  */
    66  */
    66 static int		dlmgmt_door_fd = -1;
    67 static int		dlmgmt_door_fd = -1;
    67 
    68 
    68 /*
    69 /*
    69  * This libdladm handle is global so that dlmgmt_upcall_linkprop_init()
    70  * This libdladm handle is global so that dlmgmt_upcall_linkprop_init() can
    70  * can pass to libdladm.  The handle is opened during dlmgmt_init_privileges()
    71  * pass to libdladm.  The handle is opened with "ALL" privileges, before
    71  * with "ALL" privileges.  It is not able to open DLMGMT_DOOR at that time as
    72  * privileges are dropped in dlmgmt_drop_privileges().  It is not able to open
    72  * it hasn't been created yet.  This door in the handle is opened in the first
    73  * DLMGMT_DOOR at that time as it hasn't been created yet.  This door in the
    73  * call to dladm_door_fd().
    74  * handle is opened in the first call to dladm_door_fd().
    74  */
    75  */
    75 dladm_handle_t		dld_handle = NULL;
    76 dladm_handle_t		dld_handle = NULL;
    76 
    77 
    77 static void		dlmgmtd_exit(int);
    78 static void		dlmgmtd_exit(int);
    78 static int		dlmgmt_init();
    79 static int		dlmgmt_init();
    79 static void		dlmgmt_fini();
    80 static void		dlmgmt_fini();
    80 static int		dlmgmt_init_privileges();
    81 static int		dlmgmt_set_privileges();
    81 static void		dlmgmt_fini_privileges();
       
    82 
    82 
    83 static int
    83 static int
    84 dlmgmt_set_doorfd(boolean_t start)
    84 dlmgmt_set_doorfd(boolean_t start)
    85 {
    85 {
    86 	dld_ioc_door_t did;
    86 	dld_ioc_door_t did;
    95 
    95 
    96 	return (err);
    96 	return (err);
    97 }
    97 }
    98 
    98 
    99 static int
    99 static int
   100 dlmgmt_door_init()
   100 dlmgmt_door_init(void)
   101 {
   101 {
   102 	int fd;
   102 	int err = 0;
   103 	int err;
       
   104 
       
   105 	/*
       
   106 	 * Create the door file for dlmgmtd.
       
   107 	 */
       
   108 	if ((fd = open(DLMGMT_DOOR, O_CREAT|O_RDONLY, 0644)) == -1) {
       
   109 		err = errno;
       
   110 		dlmgmt_log(LOG_ERR, "open(%s) failed: %s",
       
   111 		    DLMGMT_DOOR, strerror(err));
       
   112 		return (err);
       
   113 	}
       
   114 	(void) close(fd);
       
   115 
   103 
   116 	if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL,
   104 	if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL,
   117 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
   105 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
   118 		err = errno;
   106 		err = errno;
   119 		dlmgmt_log(LOG_ERR, "door_create() failed: %s",
   107 		dlmgmt_log(LOG_ERR, "door_create() failed: %s",
   120 		    strerror(err));
   108 		    strerror(err));
   121 		return (err);
   109 		return (err);
   122 	}
   110 	}
   123 	/*
       
   124 	 * fdetach first in case a previous daemon instance exited
       
   125 	 * ungracefully.
       
   126 	 */
       
   127 	(void) fdetach(DLMGMT_DOOR);
       
   128 	if (fattach(dlmgmt_door_fd, DLMGMT_DOOR) != 0) {
       
   129 		err = errno;
       
   130 		dlmgmt_log(LOG_ERR, "fattach(%s) failed: %s",
       
   131 		    DLMGMT_DOOR, strerror(err));
       
   132 		goto fail;
       
   133 	}
       
   134 	if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) {
       
   135 		dlmgmt_log(LOG_ERR, "cannot set kernel doorfd: %s",
       
   136 		    strerror(err));
       
   137 		(void) fdetach(DLMGMT_DOOR);
       
   138 		goto fail;
       
   139 	}
       
   140 
       
   141 	return (0);
       
   142 fail:
       
   143 	(void) door_revoke(dlmgmt_door_fd);
       
   144 	dlmgmt_door_fd = -1;
       
   145 	return (err);
   111 	return (err);
   146 }
   112 }
   147 
   113 
   148 static void
   114 static void
   149 dlmgmt_door_fini()
   115 dlmgmt_door_fini(void)
   150 {
   116 {
   151 	if (dlmgmt_door_fd == -1)
   117 	if (dlmgmt_door_fd == -1)
   152 		return;
   118 		return;
   153 
   119 
   154 	if (door_revoke(dlmgmt_door_fd) == -1) {
   120 	if (door_revoke(dlmgmt_door_fd) == -1) {
   155 		dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s",
   121 		dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s",
   156 		    DLMGMT_DOOR, strerror(errno));
   122 		    DLMGMT_DOOR, strerror(errno));
   157 	}
   123 	}
   158 
       
   159 	(void) fdetach(DLMGMT_DOOR);
       
   160 	(void) dlmgmt_set_doorfd(B_FALSE);
   124 	(void) dlmgmt_set_doorfd(B_FALSE);
   161 }
   125 	dlmgmt_door_fd = -1;
   162 
   126 }
       
   127 
       
   128 int
       
   129 dlmgmt_door_attach(zoneid_t zoneid, char *rootdir)
       
   130 {
       
   131 	int	fd;
       
   132 	int	err = 0;
       
   133 	char	doorpath[MAXPATHLEN];
       
   134 
       
   135 	(void) snprintf(doorpath, sizeof (doorpath), "%s%s", rootdir,
       
   136 	    DLMGMT_DOOR);
       
   137 
       
   138 	/*
       
   139 	 * Create the door file for dlmgmtd.
       
   140 	 */
       
   141 	if ((fd = open(doorpath, O_CREAT|O_RDONLY, 0644)) == -1) {
       
   142 		err = errno;
       
   143 		dlmgmt_log(LOG_ERR, "open(%s) failed: %s", doorpath,
       
   144 		    strerror(err));
       
   145 		return (err);
       
   146 	}
       
   147 	(void) close(fd);
       
   148 	if (chown(doorpath, UID_DLADM, GID_SYS) == -1)
       
   149 		return (errno);
       
   150 
       
   151 	/*
       
   152 	 * fdetach first in case a previous daemon instance exited
       
   153 	 * ungracefully.
       
   154 	 */
       
   155 	(void) fdetach(doorpath);
       
   156 	if (fattach(dlmgmt_door_fd, doorpath) != 0) {
       
   157 		err = errno;
       
   158 		dlmgmt_log(LOG_ERR, "fattach(%s) failed: %s", doorpath,
       
   159 		    strerror(err));
       
   160 	} else if (zoneid == GLOBAL_ZONEID) {
       
   161 		if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) {
       
   162 			dlmgmt_log(LOG_ERR, "cannot set kernel doorfd: %s",
       
   163 			    strerror(err));
       
   164 		}
       
   165 	}
       
   166 
       
   167 	return (err);
       
   168 }
       
   169 
       
   170 /*
       
   171  * Create the /etc/svc/volatile/dladm/ directory if it doesn't exist, load the
       
   172  * datalink.conf data for this zone, and create/attach the door rendezvous
       
   173  * file.
       
   174  */
       
   175 int
       
   176 dlmgmt_zone_init(zoneid_t zoneid)
       
   177 {
       
   178 	char	rootdir[MAXPATHLEN], tmpfsdir[MAXPATHLEN];
       
   179 	int	err;
       
   180 	struct stat statbuf;
       
   181 
       
   182 	if (zoneid == GLOBAL_ZONEID) {
       
   183 		rootdir[0] = '\0';
       
   184 	} else if (zone_getattr(zoneid, ZONE_ATTR_ROOT, rootdir,
       
   185 	    sizeof (rootdir)) < 0) {
       
   186 		return (errno);
       
   187 	}
       
   188 
       
   189 	/*
       
   190 	 * Create the DLMGMT_TMPFS_DIR directory.
       
   191 	 */
       
   192 	(void) snprintf(tmpfsdir, sizeof (tmpfsdir), "%s%s", rootdir,
       
   193 	    DLMGMT_TMPFS_DIR);
       
   194 	if (stat(tmpfsdir, &statbuf) < 0) {
       
   195 		if (mkdir(tmpfsdir, (mode_t)0755) < 0)
       
   196 			return (errno);
       
   197 	} else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
       
   198 		return (ENOTDIR);
       
   199 	}
       
   200 
       
   201 	if ((chmod(tmpfsdir, 0755) < 0) ||
       
   202 	    (chown(tmpfsdir, UID_DLADM, GID_SYS) < 0)) {
       
   203 		return (EPERM);
       
   204 	}
       
   205 
       
   206 	if ((err = dlmgmt_db_init(zoneid)) != 0)
       
   207 		return (err);
       
   208 	return (dlmgmt_door_attach(zoneid, rootdir));
       
   209 }
       
   210 
       
   211 /*
       
   212  * Initialize each running zone.
       
   213  */
   163 static int
   214 static int
   164 dlmgmt_init()
   215 dlmgmt_allzones_init(void)
   165 {
   216 {
   166 	int err;
   217 	int		err, i;
       
   218 	zoneid_t	*zids = NULL;
       
   219 	uint_t		nzids, nzids_saved;
       
   220 
       
   221 	if (zone_list(NULL, &nzids) != 0)
       
   222 		return (errno);
       
   223 again:
       
   224 	nzids *= 2;
       
   225 	if ((zids = malloc(nzids * sizeof (zoneid_t))) == NULL)
       
   226 		return (errno);
       
   227 	nzids_saved = nzids;
       
   228 	if (zone_list(zids, &nzids) != 0) {
       
   229 		free(zids);
       
   230 		return (errno);
       
   231 	}
       
   232 	if (nzids > nzids_saved) {
       
   233 		free(zids);
       
   234 		goto again;
       
   235 	}
       
   236 
       
   237 	for (i = 0; i < nzids; i++) {
       
   238 		if ((err = dlmgmt_zone_init(zids[i])) != 0)
       
   239 			break;
       
   240 	}
       
   241 	free(zids);
       
   242 	return (err);
       
   243 }
       
   244 
       
   245 static int
       
   246 dlmgmt_init(void)
       
   247 {
       
   248 	int	err;
       
   249 	char	*fmri, *c;
       
   250 	char	filename[MAXPATHLEN];
       
   251 
       
   252 	if (dladm_open(&dld_handle) != DLADM_STATUS_OK) {
       
   253 		dlmgmt_log(LOG_ERR, "dladm_open() failed");
       
   254 		return (EPERM);
       
   255 	}
   167 
   256 
   168 	if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR ||
   257 	if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR ||
   169 	    signal(SIGINT, dlmgmtd_exit) == SIG_ERR) {
   258 	    signal(SIGINT, dlmgmtd_exit) == SIG_ERR) {
   170 		err = errno;
   259 		err = errno;
   171 		dlmgmt_log(LOG_ERR, "signal() for SIGTERM/INT failed: %s",
   260 		dlmgmt_log(LOG_ERR, "signal() for SIGTERM/INT failed: %s",
   172 		    strerror(err));
   261 		    strerror(err));
   173 		return (err);
   262 		return (err);
   174 	}
   263 	}
   175 
   264 
   176 	if ((err = dlmgmt_linktable_init()) != 0)
   265 	/*
   177 		return (err);
   266 	 * First derive the name of the cache file from the FMRI name. This
   178 
   267 	 * cache name is used to keep active datalink configuration.
   179 	if ((err = dlmgmt_db_init()) != 0 || (err = dlmgmt_door_init()) != 0)
   268 	 */
       
   269 	if (debug) {
       
   270 		(void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s",
       
   271 		    DLMGMT_TMPFS_DIR, progname, ".debug.cache");
       
   272 	} else {
       
   273 		if ((fmri = getenv("SMF_FMRI")) == NULL) {
       
   274 			dlmgmt_log(LOG_ERR, "dlmgmtd is an smf(5) managed "
       
   275 			    "service and should not be run from the command "
       
   276 			    "line.");
       
   277 			return (EINVAL);
       
   278 		}
       
   279 
       
   280 		/*
       
   281 		 * The FMRI name is in the form of
       
   282 		 * svc:/service/service:instance.  We need to remove the
       
   283 		 * prefix "svc:/" and replace '/' with '-'.  The cache file
       
   284 		 * name is in the form of "service:instance.cache".
       
   285 		 */
       
   286 		if ((c = strchr(fmri, '/')) != NULL)
       
   287 			c++;
       
   288 		else
       
   289 			c = fmri;
       
   290 		(void) snprintf(filename, MAXPATHLEN, "%s.cache", c);
       
   291 		c = filename;
       
   292 		while ((c = strchr(c, '/')) != NULL)
       
   293 			*c = '-';
       
   294 
       
   295 		(void) snprintf(cachefile, MAXPATHLEN, "%s/%s",
       
   296 		    DLMGMT_TMPFS_DIR, filename);
       
   297 	}
       
   298 
       
   299 	dlmgmt_linktable_init();
       
   300 	if ((err = dlmgmt_door_init()) != 0)
       
   301 		goto done;
       
   302 
       
   303 	/*
       
   304 	 * Load datalink configuration and create dlmgmtd door files for all
       
   305 	 * currently running zones.
       
   306 	 */
       
   307 	if ((err = dlmgmt_allzones_init()) != 0)
       
   308 		dlmgmt_door_fini();
       
   309 
       
   310 done:
       
   311 	if (err != 0)
   180 		dlmgmt_linktable_fini();
   312 		dlmgmt_linktable_fini();
   181 
       
   182 	return (err);
   313 	return (err);
   183 }
   314 }
   184 
   315 
   185 static void
   316 static void
   186 dlmgmt_fini()
   317 dlmgmt_fini(void)
   187 {
   318 {
   188 	dlmgmt_door_fini();
   319 	dlmgmt_door_fini();
   189 	dlmgmt_linktable_fini();
   320 	dlmgmt_linktable_fini();
       
   321 	if (dld_handle != NULL) {
       
   322 		dladm_close(dld_handle);
       
   323 		dld_handle = NULL;
       
   324 	}
   190 }
   325 }
   191 
   326 
   192 /*
   327 /*
   193  * This is called by the child process to inform the parent process to
   328  * This is called by the child process to inform the parent process to
   194  * exit with the given return value.
   329  * exit with the given return value.
   212 static void
   347 static void
   213 dlmgmtd_exit(int signo)
   348 dlmgmtd_exit(int signo)
   214 {
   349 {
   215 	(void) close(pfds[1]);
   350 	(void) close(pfds[1]);
   216 	dlmgmt_fini();
   351 	dlmgmt_fini();
   217 	dlmgmt_fini_privileges();
       
   218 	exit(EXIT_FAILURE);
   352 	exit(EXIT_FAILURE);
   219 }
   353 }
   220 
   354 
   221 static void
   355 static void
   222 usage(void)
   356 usage(void)
   224 	(void) fprintf(stderr, "Usage: %s [-d]\n", progname);
   358 	(void) fprintf(stderr, "Usage: %s [-d]\n", progname);
   225 	exit(EXIT_FAILURE);
   359 	exit(EXIT_FAILURE);
   226 }
   360 }
   227 
   361 
   228 /*
   362 /*
   229  * Set the uid of this daemon to the "dladm" user. Finish the following
   363  * Restrict privileges to only those needed.
   230  * operations before setuid() because they need root privileges:
   364  */
   231  *
   365 int
   232  *    - create the /etc/svc/volatile/dladm directory;
   366 dlmgmt_drop_privileges(void)
   233  *    - change its uid/gid to "dladm"/"sys";
   367 {
   234  *    - open the dld control node
   368 	priv_set_t	*pset;
       
   369 	priv_ptype_t	ptype;
       
   370 	zoneid_t	zoneid = getzoneid();
       
   371 	int		err = 0;
       
   372 
       
   373 	if ((pset = priv_allocset()) == NULL)
       
   374 		return (errno);
       
   375 
       
   376 	/*
       
   377 	 * The global zone needs PRIV_PROC_FORK so that it can fork() when it
       
   378 	 * issues db ops in non-global zones, PRIV_SYS_CONFIG to post
       
   379 	 * sysevents, and PRIV_SYS_DL_CONFIG to initialize link properties in
       
   380 	 * dlmgmt_upcall_linkprop_init().
       
   381 	 *
       
   382 	 * We remove all privileges from the permitted (and thus effective)
       
   383 	 * set in the non-global zone.  When executing in a non-global zone,
       
   384 	 * dlmgmtd only needs to read and write to files that it already owns.
       
   385 	 */
       
   386 	priv_emptyset(pset);
       
   387 	if (zoneid == GLOBAL_ZONEID) {
       
   388 		ptype = PRIV_EFFECTIVE;
       
   389 		if (priv_addset(pset, PRIV_PROC_FORK) == -1 ||
       
   390 		    priv_addset(pset, PRIV_SYS_CONFIG) == -1 ||
       
   391 		    priv_addset(pset, PRIV_SYS_DL_CONFIG) == -1)
       
   392 			err = errno;
       
   393 	} else {
       
   394 		ptype = PRIV_PERMITTED;
       
   395 	}
       
   396 	if (err == 0 && setppriv(PRIV_SET, ptype, pset) == -1)
       
   397 		err = errno;
       
   398 done:
       
   399 	priv_freeset(pset);
       
   400 	return (err);
       
   401 }
       
   402 
       
   403 int
       
   404 dlmgmt_elevate_privileges(void)
       
   405 {
       
   406 	priv_set_t	*privset;
       
   407 	int		err = 0;
       
   408 
       
   409 	if ((privset = priv_str_to_set("zone", ",", NULL)) == NULL)
       
   410 		return (errno);
       
   411 	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, privset) == -1)
       
   412 		err = errno;
       
   413 	priv_freeset(privset);
       
   414 	return (err);
       
   415 }
       
   416 
       
   417 /*
       
   418  * Set the uid of this daemon to the "dladm" user and drop privileges to only
       
   419  * those needed.
   235  */
   420  */
   236 static int
   421 static int
   237 dlmgmt_init_privileges()
   422 dlmgmt_set_privileges(void)
   238 {
   423 {
   239 	struct stat	statbuf;
   424 	int err;
   240 
   425 
   241 	/*
   426 	(void) setgroups(0, NULL);
   242 	 * Create the DLMGMT_TMPFS_DIR directory.
   427 	if (setegid(GID_SYS) == -1 || seteuid(UID_DLADM) == -1)
   243 	 */
   428 		err = errno;
   244 	if (stat(DLMGMT_TMPFS_DIR, &statbuf) < 0) {
   429 	else
   245 		if (mkdir(DLMGMT_TMPFS_DIR, (mode_t)0755) < 0)
   430 		err = dlmgmt_drop_privileges();
   246 			return (errno);
   431 done:
   247 	} else {
   432 	return (err);
   248 		if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
       
   249 			return (ENOTDIR);
       
   250 	}
       
   251 
       
   252 	if ((chmod(DLMGMT_TMPFS_DIR, 0755) < 0) ||
       
   253 	    (chown(DLMGMT_TMPFS_DIR, UID_DLADM, GID_SYS) < 0)) {
       
   254 		return (EPERM);
       
   255 	}
       
   256 
       
   257 	/*
       
   258 	 * When dlmgmtd is started at boot, "ALL" privilege is required
       
   259 	 * to open the dld control node.  The door isn't created yet.
       
   260 	 */
       
   261 	if (dladm_open(&dld_handle) != DLADM_STATUS_OK) {
       
   262 		dlmgmt_log(LOG_ERR, "dladm_open() failed");
       
   263 		return (EPERM);
       
   264 	}
       
   265 
       
   266 	/*
       
   267 	 * We need PRIV_SYS_DL_CONFIG for the DLDIOC_DOORSERVER ioctl,
       
   268 	 * and PRIV_SYS_CONFIG to post sysevents.
       
   269 	 */
       
   270 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, UID_DLADM,
       
   271 	    GID_SYS, PRIV_SYS_DL_CONFIG, PRIV_SYS_CONFIG, NULL) == -1) {
       
   272 		dladm_close(dld_handle);
       
   273 		dld_handle = NULL;
       
   274 		return (EPERM);
       
   275 	}
       
   276 
       
   277 
       
   278 	return (0);
       
   279 }
       
   280 
       
   281 static void
       
   282 dlmgmt_fini_privileges()
       
   283 {
       
   284 	if (dld_handle != NULL) {
       
   285 		dladm_close(dld_handle);
       
   286 		dld_handle = NULL;
       
   287 	}
       
   288 }
   433 }
   289 
   434 
   290 /*
   435 /*
   291  * Keep the pfds fd open, close other fds.
   436  * Keep the pfds fd open, close other fds.
   292  */
   437  */
   345 }
   490 }
   346 
   491 
   347 int
   492 int
   348 main(int argc, char *argv[])
   493 main(int argc, char *argv[])
   349 {
   494 {
   350 	int		opt;
   495 	int opt, err;
   351 
   496 
   352 	progname = strrchr(argv[0], '/');
   497 	progname = strrchr(argv[0], '/');
   353 	if (progname != NULL)
   498 	if (progname != NULL)
   354 		progname++;
   499 		progname++;
   355 	else
   500 	else
   369 	}
   514 	}
   370 
   515 
   371 	if (!debug && !dlmgmt_daemonize())
   516 	if (!debug && !dlmgmt_daemonize())
   372 		return (EXIT_FAILURE);
   517 		return (EXIT_FAILURE);
   373 
   518 
   374 	if ((errno = dlmgmt_init_privileges()) != 0) {
   519 	if ((err = dlmgmt_init()) != 0) {
   375 		dlmgmt_log(LOG_ERR, "dlmgmt_init_privileges() failed: %s",
   520 		dlmgmt_log(LOG_ERR, "unable to initialize daemon: %s",
   376 		    strerror(errno));
   521 		    strerror(err));
   377 		goto child_out;
   522 		goto child_out;
   378 	}
   523 	} else if ((err = dlmgmt_set_privileges()) != 0) {
   379 
   524 		dlmgmt_log(LOG_ERR, "unable to set daemon privileges: %s",
   380 	if (dlmgmt_init() != 0) {
   525 		    strerror(err));
   381 		dlmgmt_fini_privileges();
   526 		dlmgmt_fini();
   382 		goto child_out;
   527 		goto child_out;
   383 	}
   528 	}
   384 
   529 
   385 	/*
   530 	/*
   386 	 * Inform the parent process that it can successfully exit.
   531 	 * Inform the parent process that it can successfully exit.