allocate3.c revision 10ddde3aee60d88fa580028fcf7642a87e80a2c6
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <auth_attr.h>
#include <auth_list.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libintl.h>
#include <locale.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <syslog.h>
#include <limits.h>
#include <user_attr.h>
#include <secdb.h>
#include <sys/resource.h>
#include <utime.h>
#include <libgen.h>
#include <zone.h>
#include <nss_dbdefs.h>
#include <bsm/devalloc.h>
#include <libdevinfo.h>
#include "allocate.h"
extern void print_error(int, char *);
#else /* !DEBUG */
#define dprintf(s, a) 0
#define dperror(s) 0
#endif /* DEBUG */
DEV_ERRORED(sbuf)))
#define ALLOC_CLEAN "-A"
#define DEALLOC_CLEAN "-D"
#define DEVICE_AUTH_SEPARATOR ","
#define LOCALDEVICE "/dev/console"
#define PROCFS "/proc/"
#define SFF_NO_ERROR 0x1
#define ALLOC_BY_NONE -1
#define CHECK_DRANGE 1
#define CHECK_URANGE 2
#define CHECK_ZLABEL 3
extern void audit_allocate_list(char *);
extern void audit_allocate_device(char *);
extern int system_labeled;
extern char *newenv[];
struct state_file {
int sf_flags;
char sf_path[MAXPATHLEN];
};
struct file_info {
char *fi_message;
};
struct zone_path {
int count;
char **path;
};
struct dev_names {
char **dnames;
};
static int remove_znode(char *, devmap_t *);
static int update_device(char **, char *, int);
/*
* checks if the invoking user is local to the device
*/
/*ARGSUSED*/
int
{
return (1);
return (0);
}
/*
* Checks if the user with the specified uid has the specified authorization
*/
int
{
char pw_buf[NSS_BUFLEN_PASSWD];
/*
* first, the easy cases
*/
return (1);
return (ALLOC_BY_NONE);
return (0);
return (0);
break;
}
}
/*
* Checks if the specified user has authorization for the device
*/
int
{
int ares;
return (0);
/* the local authorization is before the separator */
return (0);
} else
return (ares);
}
int
{
int status = 0;
char **file;
return (NODMAPERR);
break;
}
}
return (status);
}
int
{
char *p = NULL;
return (DEFATTRSERR);
}
KV_TOKEN_DELIMIT) == 0) {
*p = '\0';
}
return (0);
}
void
{
char *p = NULL;
(void) printf("auths=%s%s",
(void) printf("clean=%s%s",
KV_ASSIGN, KV_TOKEN_DELIMIT) == 0) {
*p = '\0';
}
}
else
}
(void) printf("\n");
}
void
{
char **file;
}
(void) printf("\n");
}
/* ARGSUSED */
int
{
int bytes = 0;
int error = 0;
int is_authorized = 0;
char file_name[MAXPATHLEN];
struct state_file sf;
setdmapent();
enddmapent();
dprintf("Unable to find %s in the maps database\n",
da->da_devname);
return (NODMAPERR);
}
enddmapent();
else
goto out;
}
if (system_labeled) {
dprintf("Unable to find %s device files\n",
da->da_devname);
goto out;
}
} else {
da->da_devname);
if (bytes <= 0) {
error = DEVNAMEERR;
goto out;
} else if (bytes >= MAXPATHLEN) {
dprintf("device name %s is too long.\n",
da->da_devname);
error = DEVLONGERR;
goto out;
}
}
dperror("Error:");
goto out;
}
is_authorized = 1;
else
/*
* list all free devices
*/
error = PREALLOCERR;
goto out;
}
if (system_labeled) {
/*
* for this free device, check if -
* 1. user has authorization to allocate
* 2. the zone label is within the label range of the
* device
*/
if (is_authorized == ALLOC_BY_NONE) {
goto out;
} else if (is_authorized == 0) {
goto out;
}
CHECK_DRANGE) != 0) {
error = LABELRNGERR;
goto out;
}
}
/*
* list all allocated devices
*/
goto out;
}
error = DEVSTATEERR;
goto out;
}
if (system_labeled) {
/*
* check if the zone label equals the label at which
* the device is allocated.
*/
CHECK_ZLABEL) != 0) {
error = LABELRNGERR;
goto out;
}
}
/*
* list all devices - free and allocated - available
*/
(is_authorized == ALLOC_BY_NONE)) {
/*
* don't complain if we're here for the GUI.
*/
error = 0;
goto out;
}
}
/*
* if we're not displaying in the GUI,
* check if the zone label equals the label
* at which the device is allocated.
*/
CHECK_ZLABEL) != 0) {
error = LABELRNGERR;
goto out;
}
}
/*
* if we're not displaying in the GUI,
* for this free device, check if -
* 1. user has authorization to allocate
* 2. the zone label is within the label range of the
* device
*/
if (is_authorized == ALLOC_BY_NONE) {
goto out;
} else if (is_authorized == 0) {
goto out;
}
CHECK_DRANGE) != 0) {
error = LABELRNGERR;
goto out;
}
}
}
error = DEVSTATEERR;
goto out;
}
error = DSPMISSERR;
goto out;
}
else
error = 0;
out:
return (error);
}
/* ARGSUSED */
int
{
int error = 0;
devalloc_t *da;
/*
* Private interface for GUI.
*/
(void) puts(DA_DB_LOCK);
return (0);
}
/*
* we need device.revoke to list someone else's devices
*/
return (UAUTHERR);
}
if (system_labeled) {
/*
* we need device.allocate to list our devices
*/
return (UAUTHERR);
/*
* list default attrs from devalloc_defaults
*/
setdadefent();
if (device) {
/*
* list default attrs for this device type
*/
enddadefent();
dprintf("No default attributes for "
"%s\n", device);
return (DEFATTRSERR);
}
} else {
/*
* list everything in devalloc_defaults
*/
(void) print_da_defs(da_defs);
}
}
enddadefent();
return (error);
}
}
/*
* Lock the database to make sure no body writes to it while we are
* reading.
*/
setdaent();
if (device) {
/*
* list all devices of this class.
*/
zonename);
}
}
} else {
/*
* list this device
*/
enddaent();
return (NODAERR);
}
}
} else {
/*
* list all devices
*/
}
}
enddaent();
return (error);
}
/*
* Set the DAC characteristics of the file.
* This uses a fancy chmod() by setting a minimal ACL which sets the mode
* and discards any existing ACL.
*/
int
{
int err = 0;
if (mode == ALLOC_MODE) {
dperror("newdac: unable to chown");
}
} else do {
dperror("newdac: unable to chown");
}
if (err)
return (err);
/*
* This could be a SunRay device that is in /tmp.
*/
dperror("newdac: unable to chmod");
}
} else {
}
if (err != 0) {
dperror("newdac: unable to setacl");
}
return (err);
}
/*
* lock_dev -
* locks a section of DA_DB_LOCK.
* returns lock fd if successful, else -1 on error.
*/
static int
{
static int lockfd = -1;
int ret;
int count = 0;
int retry = 10;
char *lockfile;
if (system_labeled)
else
if (statbuf) {
} else {
offset = 0;
}
if ((lockfd == -1) &&
dperror("lock_dev: cannot open lock file");
return (-1);
}
if (system_labeled) {
dperror("lock_dev: cannot position lock file");
return (-1);
}
size = 1;
}
errno = 0;
while (retry) {
count++;
if (ret == 0)
return (lockfd);
dperror("lock_dev: cannot set lock");
return (-1);
}
retry--;
errno = 0;
}
return (-1);
}
int
{
int i;
int error = 0;
char **file;
return (NODMAPERR);
break;
}
}
/*
* mark as allocated any new device nodes that we
* created in local zone
*/
mode)) != 0) {
break;
}
}
}
return (error);
}
/*
* working with the file, even "vold" (bug #4095152).
*/
int
{
int fuserpid;
char buf[MAXPATHLEN];
/*
* vfork() and execl() just to make the same output
* as before fixing of bug #4095152.
* The problem is that the "fuser" command prints
* one part of output into stderr and another into stdout,
* but user sees them mixed. Of course, better to change "fuser"
* or to intercept and not to print its output.
*/
if (c_pid == -1)
return (-1);
if (c_pid == 0) {
dperror("first exec fuser");
_exit(1);
}
if (WEXITSTATUS(lock) != 0)
return (-1);
}
if (pipe(p)) {
dperror("pipe");
return (-1);
}
/* vfork() and execl() to catch output and to process it */
if (c_pid == -1) {
dperror("second vfork");
return (-1);
}
if (c_pid == 0) {
(void) close(p[0]);
(void) close(1);
(void) close(p[1]);
(void) close(2);
dperror("second exec fuser");
_exit(1);
}
(void) close(p[1]);
fuserpid);
continue;
}
(char *)&info) == -1) {
dperror("");
continue;
}
dprintf("%d matched vold name\n",
fuserpid);
continue;
}
if ((r =
dperror("");
break;
}
}
}
} else {
dperror("fdopen(p[0], r)");
r = -1;
}
return (r);
}
int
{
int error = 0;
int status;
char **file;
return (NODMAPERR);
dperror("");
}
if (error == 0)
}
return (error);
}
int
{
int status = 0;
char **file;
return (NODMAPERR);
}
return (status);
}
int
char *clean_arg)
{
int c;
int status = 0, exit_status;
char zonepath[MAXPATHLEN];
char title[100];
char pw_buf[NSS_BUFLEN_PASSWD];
zonepath[0] = '\0';
if (system_labeled) {
return (-1);
} else {
dprintf("unable to get label for %s zone\n",
zonename);
return (-1);
}
} else {
}
}
return (-1);
mode = "-I";
mode = "-f";
else
mode = "-s";
return (0);
else
cmd++; /* skip leading '/' */
c = vfork();
switch (c) {
case -1:
return (-1);
case 0:
(void) setuid(0);
/* First try .windowing version of script */
/* If that failed, run regular version via dtterm */
"Device %s for %s",
"-e", "/etc/security/lib/wdwwrapper",
/*
* And if that failed, continue on to try
* running regular version directly.
*/
}
if (system_labeled) {
} else {
}
dperror("");
default:
dprintf("Child %d", c);
return (exit_status);
} else if (WIFSIGNALED(status)) {
} else {
}
return (-1);
}
}
int
{
int bytes = 0;
int error = 0;
int is_authorized = 0;
char file_name[MAXPATHLEN];
struct state_file sf;
setdmapent();
enddmapent();
dprintf("Unable to find %s in device map database\n",
da->da_devname);
return (NODMAPERR);
}
enddmapent();
} else {
}
if (system_labeled) {
if (dm_new)
dprintf("Unable to find %s device files\n",
da->da_devname);
goto out;
}
} else {
da->da_devname);
if (bytes <= 0) {
error = DEVNAMEERR;
goto out;
} else if (bytes >= MAXPATHLEN) {
dprintf("device name %s is too long.\n",
da->da_devname);
error = DEVLONGERR;
goto out;
}
}
goto out;
}
goto out;
}
if (system_labeled) {
/*
* unless we're here to deallocate by force, check if the
* label at which the device is currently allocated is
* within the user label range.
*/
error = LABELRNGERR;
goto out;
}
}
goto out;
}
if (!DEV_ALLOCATED(stat_buf)) {
if (DEV_ERRORED(stat_buf)) {
error = DEVSTATEERR;
goto out;
}
} else {
goto out;
}
}
/* All checks passed, time to lock and deallocate */
goto out;
}
if (system_labeled) {
goto out;
}
}
}
goto out;
}
if (system_labeled == 0) {
DEALLOC_MODE)) != 0) {
goto out;
}
}
/*
* if we are deallocating device owned by someone else,
* pass the owner's uid to the cleaning script.
*/
if (error != 0) {
} else {
error = 0;
}
}
out:
if (dm_new)
return (error);
}
int
int *lock_fd)
{
int i;
int bytes = 0;
int error = 0;
int is_authorized = 0;
int dealloc_optflag = 0;
char file_name[MAXPATHLEN];
struct state_file sf;
setdmapent();
enddmapent();
dprintf("Unable to find %s in device map database\n",
da->da_devname);
return (NODMAPERR);
}
enddmapent();
if (system_labeled) {
dprintf("Unable to find %s device files\n",
da->da_devname);
goto out;
}
} else {
da->da_devname);
if (bytes <= 0) {
error = DEVNAMEERR;
goto out;
} else if (bytes >= MAXPATHLEN) {
dprintf("device name %s is too long.\n",
da->da_devname);
error = DEVLONGERR;
goto out;
}
}
(void) audit_allocate_device(fname);
dperror("Error:");
goto out;
}
if (DEV_ERRORED(stat_buf)) {
error = DEVSTATEERR;
goto out;
}
if (is_authorized == ALLOC_BY_NONE) {
goto out;
goto out;
}
if (system_labeled) {
/*
* check if label of the zone to which the device is being
* allocated is within the device label range.
*/
error = LABELRNGERR;
goto out;
}
}
error = DSPMISSERR;
goto out;
}
if (DEV_ALLOCATED(stat_buf)) {
else
dprintf("Couldn't force deallocate device %s\n",
da->da_devname);
goto out;
}
error = PREALLOCERR;
goto out;
} else {
goto out;
}
}
/* All checks passed, time to lock and allocate */
goto out;
}
if (system_labeled) {
/*
* Run the cleaning program; it also mounts allocated
* device if required.
*/
if (error != DEVCLEAN_OK) {
switch (error) {
case DEVCLEAN_ERROR:
case DEVCLEAN_SYSERR:
dprintf("allocate: "
"Error in device clean program %s\n",
da->da_devexec);
goto out;
case DEVCLEAN_BADMOUNT:
dprintf("allocate: Failed to mount device %s\n",
da->da_devexec);
goto out;
case DEVCLEAN_MOUNTOK:
break;
default:
error = 0;
goto out;
}
}
/*
* If not mounted, create zonelinks, if this is not the
* global zone.
*/
(error != DEVCLEAN_MOUNTOK)) {
goto out;
}
}
}
goto out;
}
if (system_labeled == 0) {
ALLOC_MODE)) != 0) {
goto out;
}
}
error = 0;
out:
}
return (error);
}
void
{
int i;
(*count + 1) * sizeof (char *));
if (da) {
(*count)++;
} else {
if (flag == DA_ADD_ZONE)
else if (flag == DA_REMOVE_ZONE)
for (i = 0; i < *count; i++)
}
}
int
{
int count = 0;
int error = 0;
int lock_fd = -1;
devalloc_t *da;
return (UAUTHERR);
}
setdaent();
/*
* allocate devices of this type
*/
if (system_labeled &&
continue;
}
&lock_fd);
if (system_labeled && (error == 0)) {
/*
* we need to record in device_allocate the
* label (zone name) at which this device is
* being allocated. store this device entry.
*/
}
error = 0;
}
} else {
/*
* allocate this device
*/
enddaent();
return (NODAERR);
}
return (LOGINDEVPERMERR);
}
/*
* we need to record in device_allocate the label (zone name)
* at which this device is being allocated. store this device
* entry.
*/
if (system_labeled && (error == 0))
if (error == DEVCLEAN_BADMOUNT)
error = 0;
}
enddaent();
if (lock_fd != -1)
/*
* add to device_allocate labels (zone names) for the devices we
* allocated.
*/
return (error);
}
/* ARGSUSED */
int
{
int count = 0;
int error = 0;
int lock_fd = -1;
devalloc_t *da;
return (UAUTHERR);
}
setdaent();
/*
* deallocate all devices
*/
if (system_labeled &&
continue;
}
if (system_labeled && (error == 0)) {
/*
* we need to remove this device's allocation
* label (zone name) from device_allocate.
* store this device name.
*/
}
error = 0;
}
/*
* deallocate all devices of this type
*/
continue;
}
if (error == 0) {
/*
* we need to remove this device's allocation
* label (zone name) from device_allocate.
* store this device name.
*/
}
error = 0;
}
/*
* deallocate all devices of this class (for sunray)
*/
dprintf("trying to deallocate %s\n",
da->da_devname);
if (error == 0) {
/*
* we need to remove this device's
* allocation label (zone name) from
* device_allocate. store this device
* name.
*/
da, 0);
}
error = 0;
}
}
/*
* deallocate this device
*/
enddaent();
return (NODAERR);
}
return (LOGINDEVPERMERR);
}
&lock_fd);
if (system_labeled && (error == 0)) {
/*
* we need to remove this device's allocation label
* (zone name) from device_allocate. store this
* device name.
*/
}
if (error == DEVCLEAN_BADMOUNT)
error = 0;
}
enddaent();
if (lock_fd != -1)
/*
* remove from device_allocate labels (zone names) for the devices we
* deallocated.
*/
return (error);
}
static int
{
/* if devlist is generated, never leave device in error state */
return (NODMAPERR);
dprintf("dev_file_name: no device list for %s\n",
dm->dmap_devname);
return (NODMAPERR);
}
return (0);
}
/*
* _check_label -
* checks the device label range against zone label, which is also
* user's current label.
* returns 0 if in range, -1 for all other conditions.
*
*/
static int
{
int err;
int in_range = 0;
char pw_buf[NSS_BUFLEN_PASSWD];
return (-1);
return (-1);
}
if (flag == CHECK_DRANGE) {
&err) == 0) {
dprintf("bad min_label for device %s\n",
da->da_devname);
return (-1);
}
&err) == 0) {
dprintf("bad max_label for device %s\n",
da->da_devname);
return (-1);
}
return (-1);
}
} else if (flag == CHECK_URANGE) {
dprintf("Unable to get passwd entry for userid %d\n",
(int)uid);
return (-1);
}
dprintf("Unable to get label range for userid %d\n",
(int)uid);
return (-1);
}
if (in_range == 0) {
return (-1);
}
} else if (flag == CHECK_ZLABEL) {
return (-1);
}
return (-1);
}
}
return (0);
}
int
{
int size;
int len = 0;
int fcount = 0;
char **file;
char zonepath[MAXPATHLEN];
return (NODMAPERR);
return (1);
}
return (1);
}
/*
* First time initialization
*/
/*
* Most devices have pathnames starting in /dev
* but SunRay devices do not. In SRRS 3.1 they use /tmp.
*
* If the device pathname is not in /dev then create
* a symbolic link to it and put the device in /dev
*/
char *linkdir;
char srclinkdir[MAXPATHLEN];
char dstlinkdir[MAXPATHLEN];
*p = '\0';
*p = '/';
}
return (1);
}
dprintf("Buffer overflow in create_znode for %s\n",
*file);
return (1);
}
fcount++;
return (1);
}
}
if (di_prof_commit(prof))
return (0);
}
int
{
int len = 0;
char *zoneroot;
char **file;
char zonepath[MAXPATHLEN];
return (NODMAPERR);
} else {
}
/*
* To support SunRay we will just deal with the
* file in /dev, not the symlinks.
*/
return (1);
}
char *devrelpath;
/*
* remove device node from zone.
*
* SunRay devices don't start with /dev
* so skip over first directory to make
* sure it is /dev. SunRay devices in zones
* will have a symlink into /dev but
* we don't ever delete it.
*/
return (1);
}
}
if (di_prof_commit(prof))
return (0);
}
int
{
return (-1);
zonename);
}
if (optstr)
return (rc);
}