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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <assert.h> 29 #include <sys/types.h> 30 #include <sys/acctctl.h> 31 #include <sys/param.h> 32 #include <sys/stat.h> 33 #include <libintl.h> 34 #include <string.h> 35 #include <stdlib.h> 36 #include <stdarg.h> 37 #include <stdio.h> 38 #include <strings.h> 39 #include <unistd.h> 40 #include <errno.h> 41 #include <exacct.h> 42 #include <fcntl.h> 43 #include <priv.h> 44 45 #include "utils.h" 46 47 static char PNAME_FMT[] = "%s: "; 48 static char ERRNO_FMT[] = ": %s\n"; 49 50 static char *pname; 51 52 /*PRINTFLIKE1*/ 53 void 54 warn(const char *format, ...) 55 { 56 int err = errno; 57 va_list alist; 58 if (pname != NULL) 59 (void) fprintf(stderr, gettext(PNAME_FMT), pname); 60 va_start(alist, format); 61 (void) vfprintf(stderr, format, alist); 62 va_end(alist); 63 if (strchr(format, '\n') == NULL) 64 (void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err)); 65 } 66 67 /*PRINTFLIKE1*/ 68 void 69 die(char *format, ...) 70 { 71 int err = errno; 72 va_list alist; 73 74 if (pname != NULL) 75 (void) fprintf(stderr, gettext(PNAME_FMT), pname); 76 va_start(alist, format); 77 (void) vfprintf(stderr, format, alist); 78 va_end(alist); 79 if (strchr(format, '\n') == NULL) 80 (void) fprintf(stderr, gettext(ERRNO_FMT), strerror(err)); 81 exit(E_ERROR); 82 } 83 84 char * 85 setprogname(char *arg0) 86 { 87 char *p = strrchr(arg0, '/'); 88 89 if (p == NULL) 90 p = arg0; 91 else 92 p++; 93 pname = p; 94 return (pname); 95 } 96 97 /* 98 * Return the localized name of an accounting type. 99 */ 100 const char * 101 ac_type_name(int type) 102 { 103 switch (type) { 104 case AC_PROC: 105 return (gettext("process")); 106 case AC_FLOW: 107 return (gettext("flow")); 108 case AC_TASK: 109 return (gettext("task")); 110 default: 111 die(gettext("invalid type %d\n"), type); 112 } 113 /* NOTREACHED */ 114 return (NULL); 115 } 116 117 /* 118 * Open an accounting file. The filename specified must be an absolute 119 * pathname and the existing contents of the file (if any) must be of the 120 * requested type. Needs euid 0 to open the root-owned accounting file. 121 * file_dac_write is required to create a new file in a directory not owned 122 * by root (/var/adm/exacct is owned by 'adm'). Assumes sys_acct privilege is 123 * already asserted by caller. 124 */ 125 int 126 open_exacct_file(const char *file, int type) 127 { 128 int rc; 129 int err; 130 131 if (file[0] != '/') { 132 warn(gettext("%s is not an absolute pathname\n"), file); 133 return (-1); 134 } 135 if (!verify_exacct_file(file, type)) { 136 warn(gettext("%s is not a %s accounting file\n"), file, 137 ac_type_name(type)); 138 return (-1); 139 } 140 if (seteuid(0) == -1 || setegid(0) == -1) { 141 warn(gettext("seteuid()/setegid() failed")); 142 return (-1); 143 } 144 assert(priv_ineffect(PRIV_SYS_ACCT)); 145 (void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_FILE_DAC_WRITE, NULL); 146 rc = acctctl(type | AC_FILE_SET, (void *) file, strlen(file) + 1); 147 if (rc == -1 && (err = errno) == EBUSY) { 148 char name[MAXPATHLEN]; 149 struct stat cur; 150 struct stat new; 151 152 /* 153 * The file is already open as an accounting file somewhere. 154 * If the file we're trying to open is the same as we have 155 * currently open then we're ok. 156 */ 157 if (acctctl(type | AC_FILE_GET, name, sizeof (name)) == 0 && 158 stat(file, &new) != -1 && stat(name, &cur) != -1 && 159 new.st_dev == cur.st_dev && new.st_ino == cur.st_ino) 160 rc = 0; 161 } 162 163 /* 164 * euid 0, egid 0 and the file_dac_write privilege are no longer 165 * required; give them up permanently. 166 */ 167 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_DAC_WRITE, NULL); 168 if (setreuid(getuid(), getuid()) == -1 || 169 setregid(getgid(), getgid()) == -1) 170 die(gettext("setreuid()/setregid() failed")); 171 if (rc == 0) 172 return (0); 173 174 warn(gettext("cannot open %s accounting file %s: %s\n"), 175 ac_type_name(type), file, strerror(err)); 176 return (-1); 177 } 178 179 /* 180 * Verify that the file contents (if any) are extended accounting records 181 * of the desired type. 182 */ 183 boolean_t 184 verify_exacct_file(const char *file, int type) 185 { 186 ea_file_t ef; 187 ea_object_t eo; 188 struct stat st; 189 int err; 190 191 if (stat(file, &st) != -1 && st.st_size != 0) { 192 if (seteuid(0) == -1) 193 return (B_FALSE); 194 err = ea_open(&ef, file, "SunOS", EO_TAIL, O_RDONLY, 0); 195 if (seteuid(getuid()) == 1) 196 die(gettext("seteuid() failed")); 197 if (err == -1) 198 return (B_FALSE); 199 200 bzero(&eo, sizeof (eo)); 201 if (ea_previous_object(&ef, &eo) == EO_ERROR) { 202 /* 203 * EXR_EOF indicates there are no non-header objects 204 * in the file. It can't be determined that this 205 * file is or is not the proper type of extended 206 * accounting file, which isn't necessarily an error. 207 * Since it is a proper (albeit empty) extended 208 * accounting file, it matches any desired type. 209 * 210 * if ea_previous_object() failed for any other reason 211 * than EXR_EOF, the file must be corrupt. 212 */ 213 if (ea_error() != EXR_EOF) { 214 (void) ea_close(&ef); 215 return (B_FALSE); 216 } 217 } else { 218 /* 219 * A non-header object exists. Insist that it be 220 * either a process, task, or flow accounting record, 221 * the same type as is desired. 222 */ 223 uint_t c = eo.eo_catalog & EXD_DATA_MASK; 224 225 if (eo.eo_type != EO_GROUP || 226 (eo.eo_catalog & EXC_CATALOG_MASK) != EXC_NONE || 227 (!(c == EXD_GROUP_PROC && type == AC_PROC || 228 c == EXD_GROUP_TASK && type == AC_TASK || 229 c == EXD_GROUP_FLOW && type == AC_FLOW))) { 230 (void) ea_close(&ef); 231 return (B_FALSE); 232 } 233 } 234 (void) ea_close(&ef); 235 } 236 return (B_TRUE); 237 } 238