1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <assert.h> 27 #include <sys/types.h> 28 #include <sys/acctctl.h> 29 #include <sys/param.h> 30 #include <sys/stat.h> 31 #include <libintl.h> 32 #include <string.h> 33 #include <stdlib.h> 34 #include <stdarg.h> 35 #include <stdio.h> 36 #include <strings.h> 37 #include <unistd.h> 38 #include <errno.h> 39 #include <exacct.h> 40 #include <fcntl.h> 41 #include <priv.h> 42 43 #include "utils.h" 44 45 static char PNAME_FMT[] = "%s: "; 46 static char ERRNO_FMT[] = ": %s\n"; 47 48 static char *pname; 49 50 /*PRINTFLIKE1*/ 51 void 52 warn(const char *format, ...) 53 { 54 int err = errno; 55 va_list alist; 56 if (pname != NULL) 57 (void) fprintf(stderr, gettext(PNAME_FMT), pname); 58 va_start(alist, format); 59 (void) vfprintf(stderr, format, alist); 60 va_end(alist); 61 if (strchr(format, '\n') == NULL) 62 (void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err)); 63 } 64 65 /*PRINTFLIKE1*/ 66 void 67 die(char *format, ...) 68 { 69 int err = errno; 70 va_list alist; 71 72 if (pname != NULL) 73 (void) fprintf(stderr, gettext(PNAME_FMT), pname); 74 va_start(alist, format); 75 (void) vfprintf(stderr, format, alist); 76 va_end(alist); 77 if (strchr(format, '\n') == NULL) 78 (void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err)); 79 80 /* close the libdladm handle if it was opened */ 81 if (dld_handle != NULL) 82 dladm_close(dld_handle); 83 84 exit(E_ERROR); 85 } 86 87 char * 88 setpname(char *arg0) 89 { 90 char *p = strrchr(arg0, '/'); 91 92 if (p == NULL) 93 p = arg0; 94 else 95 p++; 96 pname = p; 97 return (pname); 98 } 99 100 /* 101 * Return the localized name of an accounting type. 102 */ 103 const char * 104 ac_type_name(int type) 105 { 106 switch (type) { 107 case AC_PROC: 108 return (gettext("process")); 109 case AC_FLOW: 110 return (gettext("flow")); 111 case AC_TASK: 112 return (gettext("task")); 113 case AC_NET: 114 return (gettext("net")); 115 default: 116 die(gettext("invalid type %d\n"), type); 117 } 118 /* NOTREACHED */ 119 return (NULL); 120 } 121 122 /* 123 * Open an accounting file. The filename specified must be an absolute 124 * pathname and the existing contents of the file (if any) must be of the 125 * requested type. Needs euid 0 to open the root-owned accounting file. 126 * file_dac_write is required to create a new file in a directory not owned 127 * by root (/var/adm/exacct is owned by 'adm'). Assumes sys_acct privilege is 128 * already asserted by caller. 129 */ 130 int 131 open_exacct_file(const char *file, int type) 132 { 133 int rc; 134 int err; 135 136 if (file[0] != '/') { 137 warn(gettext("%s is not an absolute pathname\n"), file); 138 return (-1); 139 } 140 if (!verify_exacct_file(file, type)) { 141 warn(gettext("%s is not a %s accounting file\n"), file, 142 ac_type_name(type)); 143 return (-1); 144 } 145 if (seteuid(0) == -1 || setegid(0) == -1) { 146 warn(gettext("seteuid()/setegid() failed")); 147 return (-1); 148 } 149 assert(priv_ineffect(PRIV_SYS_ACCT)); 150 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL); 151 rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1); 152 if (rc == -1 && (err = errno) == EBUSY) { 153 char name[MAXPATHLEN]; 154 struct stat cur; 155 struct stat new; 156 157 /* 158 * The file is already open as an accounting file somewhere. 159 * If the file we're trying to open is the same as we have 160 * currently open then we're ok. 161 */ 162 if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 && 163 stat(file, &new) != -1 && stat(name, &cur) != -1 && 164 new.st_dev == cur.st_dev && new.st_ino == cur.st_ino) 165 rc = 0; 166 } 167 168 /* 169 * euid 0, egid 0 and the file_dac_write privilege are no longer 170 * required; give them up permanently. 171 */ 172 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL); 173 if (setreuid(getuid(), getuid()) == -1 || 174 setregid(getgid(), getgid()) == -1) 175 die(gettext("setreuid()/setregid() failed")); 176 if (rc == 0) 177 return (0); 178 179 warn(gettext("cannot open %s accounting file %s: %s\n"), 180 ac_type_name(type), file, strerror(err)); 181 return (-1); 182 } 183 184 /* 185 * Verify that the file contents (if any) are extended accounting records 186 * of the desired type. 187 */ 188 boolean_t 189 verify_exacct_file(const char *file, int type) 190 { 191 ea_file_t ef; 192 ea_object_t eo; 193 struct stat st; 194 int err; 195 196 if (stat(file, &st) != -1 && st.st_size != 0) { 197 if (seteuid(0) == -1) 198 return (B_FALSE); 199 err = ea_open(&ef, file, "SunOS", EO_TAIL, O_RDONLY, 0); 200 if (seteuid(getuid()) == 1) 201 die(gettext("seteuid() failed")); 202 if (err == -1) 203 return (B_FALSE); 204 205 bzero(&eo, sizeof (eo)); 206 if (ea_previous_object(&ef, &eo) == EO_ERROR) { 207 /* 208 * EXR_EOF indicates there are no non-header objects 209 * in the file. It can't be determined that this 210 * file is or is not the proper type of extended 211 * accounting file, which isn't necessarily an error. 212 * Since it is a proper (albeit empty) extended 213 * accounting file, it matches any desired type. 214 * 215 * if ea_previous_object() failed for any other reason 216 * than EXR_EOF, the file must be corrupt. 217 */ 218 if (ea_error() != EXR_EOF) { 219 (void) ea_close(&ef); 220 return (B_FALSE); 221 } 222 } else { 223 /* 224 * A non-header object exists. Insist that it be 225 * either a process, task, flow or net accounting 226 * record, the same type as is desired. 227 * xxx-venu:check 101 merge for EXD_GROUP_NET_* 228 */ 229 uint_t c = eo.eo_catalog & EXD_DATA_MASK; 230 231 if (eo.eo_type != EO_GROUP || 232 (eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE || 233 (!(c == EXD_GROUP_PROC && type == AC_PROC || 234 c == EXD_GROUP_TASK && type == AC_TASK || 235 c == EXD_GROUP_FLOW && type == AC_FLOW || 236 (c == EXD_GROUP_NET_LINK_DESC || 237 c == EXD_GROUP_NET_FLOW_DESC || 238 c == EXD_GROUP_NET_LINK_STATS || 239 c == EXD_GROUP_NET_FLOW_STATS) && 240 type == AC_NET))) { 241 (void) ea_close(&ef); 242 return (B_FALSE); 243 } 244 } 245 (void) ea_close(&ef); 246 } 247 return (B_TRUE); 248 } 249