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. |
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 */ |