| 
 | 
The loge.c program allows users who have the appropriate authorization to execute a command with super-user privileges. Users must supply a password for verification. They do not need to supply the super-user password. loge.c can be used with any command whose executable file is in the /tcb/files/valhalla directory. Each command can also define a secondary subsystem authorization.
The logged-in user (LUID) is authorized if they have either the ``wotan'' primary subsystem authorization or the secondary subsystem authorization defined for the command issued under loge.c.
This program must be installed with both setuid root and setgid wotan.
This program demonstrates how to:
Compile and install this program with the following command:
cc loge.c -lprot -lx -o loge
chown root loge
chgrp wotan loge
chmod 6111 loge
1 #define SecureWare /* enable security features */2 #include <sys/types.h> /* system pseudo-types */ 3 #include <errno.h> /* system error definitions */ 4 #include <stdio.h> /* standard I/O definitions */ 5 #include <ctype.h> /* character classification */ 6 #include <string.h> /* string library definitions */ 7 #include <stdarg.h> /* variable-len argument lists */ 8 #include <unistd.h> /* access(S) definitions */ 9 #include <sys/stat.h> /* stat(S) definitions */ 10 #include <time.h> /* localtime() definitions */ 11 #include <sys/security.h> /* various security definitions */ 12 #include <sys/audit.h> /* audit definitions */ 13 #include <prot.h> /* database definitions */
14 extern char *ttyname(int), *defread(char *); 15 extern int defopen(char *);
16 #define TRUE 1 17 #define FALSE 0
18 #define ARBSTRSIZ 300 /* ARBitrary STRing SIZe */ 19 #define MAXCMDLEN ARBSTRSIZ /* MAXimum CoMmanD name LENgth */
20 char primary_subsystem[] = "wotan"; 21 char valhalla[] = "/tcb/files/valhalla"; 22 char su_defaults[] = "/etc/default/su";
23 void syserror(int, char *, ...);
24 void sulog(char *, int, char **); 25 void failed(int, char *, char **);
loge.c (Part 2/17)
26 #define TCB_ANY_TYPE ' ' /* any type of file */ 27 #define TCB_FILE 'r' /* regular file */ 28 #define TCB_DIRECTORY 'd' /* directory */
29 int check_TCB_path(char *, char, char *); 30 int check_DAC_path(char *, char, char *);
31 enum DAC_status { 32 NotListed, /* file not listed in File Control database */ 33 NoSuchFile, /* file does not exist */ 34 WrongDAC, /* existing file does not meet required DAC */ 35 MatchesDAC, /* existing file has exactly the required DAC */ 36 ExceedsDAC /* existing file grants less access than required */ 37 } compare_DAC_attributes(char *, struct pr_file *);
38 main( 39 int argc, 40 char *argv[] 41 ) { 42 struct pr_passwd *pr_pw; /* Protected Password entry */ 43 char path[sizeof(valhalla) + 1 + MAXCMDLEN + 1]; 44 char temp[sizeof(path) + ARBSTRSIZ]; 45 char *clear, *cipher; /* Clear-, cipher-text password */ 46 int password_required; /* TRUE if must have a password */ 47 int has_password; /* TRUE if has a password */ 48 int is_proper; /* TRUE if knows password */
loge.c (Part 3/17)
49 set_auth_parameters(argc, argv);
50 if (argc < 2 || *argv[1] == ' ') { 51 (void) fprintf(stderr, 52 "Usage: %s cmd [ args ]...\n", 53 command_name 54 ); 55 failed(2, (char *)0, &argv[1]); 56 /* NOTREACHED */ 57 } 58 if (strchr(argv[1], '/') != (char *)0 || strlen(argv[1]) > MAXCMDLEN) { 59 (void) fprintf(stderr, 60 "%s: Not the name of a command: %s\n", 61 command_name, argv[1] 62 ); 63 failed(1, (char *)0, &argv[1]); 64 /* NOTREACHED */ 65 }
loge.c (Part 4/17)
 66 	if ( ! authorized_user(primary_subsystem) && ! authorized_user(argv[1])) {
 67 		(void) fprintf(stderr,
 68 			"Sorry, not authorized to run %s.\n",
 69 			argv[1]
 70 		);
 71 		failed(1, (char *)0, &argv[1]);
 72 		/* NOTREACHED */
 73 	}
 74 	(void) sprintf(path, "%s/%s", valhalla, argv[1]);
 75 	if (eaccess(path, X_OK)) {
 76 		syserror(errno, "%s is not an executable command", path);
 77 		failed(1, (char *)0, &argv[1]);
 78 		/* NOTREACHED */
 79 	}
loge.c (Part 5/17)
 80 	if (check_TCB_path(path, TCB_FILE, "restricted command")) {
 81 		(void) fprintf(stderr,
 82 			"%s: The system's integrity may be compromised.\n",
 83 			command_name
 84 		);
 85 		failed(1, (char *)0, &argv[1]);
 86 		/* NOTREACHED */
 87 	}
 88 	if ((pr_pw = getprpwuid(starting_luid())) == (struct pr_passwd *)0 ||
 89 	    ! pr_pw->uflg.fg_name
 90 	) {
 91 		(void) sprintf(temp, "UID %d", starting_luid());
 92 		audit_auth_entry(temp, OT_PRPWD,
 93 			"User missing from Protected Password database"
 94 		);
 95 		(void) fprintf(stderr,
 96 			"%s: Cannot obtain authentication data for %s",
 97 			command_name, temp
 98 		);
 99 		failed(1, (char *)0, &argv[1]);
100 		/* NOTREACHED */
101 	}
102 #ifdef AUTH_U_NULLPW			/* #define'd in 3.2v2.0 	*/
103 	if (pr_pw->uflg.fg_nullpw)
104 		password_required = ( ! pr_pw->ufld.fd_nullpw);
105 	else if (pr_pw->sflg.fg_nullpw)
106 		password_required = ( ! pr_pw->sfld.fd_nullpw);
107 	else
108 #endif
109 		password_required = TRUE;
110 	if (pr_pw->uflg.fg_encrypt && *pr_pw->ufld.fd_encrypt) {
111 		has_password = TRUE;
112 		cipher = pr_pw->ufld.fd_encrypt;
113 	}
114 	else {
115 		has_password = FALSE;
116 		cipher = "*";		/* not a valid encrypted password */
117 	}
loge.c (Part 6/17)
118 if (password_required || has_password) { 119 clear = getpasswd("Password:", AUTH_MAX_PASSWD_LENGTH); 120 is_proper = (strcmp(cipher, bigcrypt(clear, cipher)) == 0); 121 (void) getpasswd((char *)0, AUTH_MAX_PASSWD_LENGTH); 122 if (is_proper) { 123 (void) sprintf(temp, 124 "Password correct - run %s as superuser", 125 path 126 ); 127 sa_audit_subsystem(ST_AUTH, "Check password", temp); 128 } 129 else { 130 (void) sprintf(temp, 131 "Password incorrect - command %s not run", 132 path 133 ); 134 sa_audit_subsystem(ST_AUTH, "Check password", temp); 135 (void) fputs("Sorry.\n", stderr); 136 failed(1, pr_pw->ufld.fd_name, &argv[1]); 137 /* NOTREACHED */ 138 } 139 }
140 sulog(pr_pw->ufld.fd_name, TRUE, &argv[1]);
141 endprpwent(); /* Close Protected Password database */ 142 endprfient(); /* Close File Control database */
143 if (putenv("PATH=/bin:/etc:/usr/bin:/tcb/bin") || putenv("IFS= \t\n")) 144 audit_no_resource("environment", OT_MEMORY, "cannot change"); 145 else if (setgid(starting_rgid())) 146 syserror(errno, "Cannot reset GID"); 147 else { 148 (void) execv(path, &argv[1]); 149 syserror(errno, "Could not run %s", path); 150 } 151 exit(3); 152 /* NOTREACHED */ 153 }
bigcrypt( ) is a function that has the same parameters and function as the traditional crypt( ) library routine. In addition, it implements an extension to the crypt function to support longer passwords.\*(F
The password encryption scheme on SCO OpenServer is designed to have compatibility with earlier UNIX system and XENIX password encryption, and to have the ability to use passwords with more than 8 significant characters. Long passwords allow the use of ``pass-phrases'' such as This is my password string. This allows easy to remember passwords and reduces the risk of a password being comprised by a dictionary-based attack. In XENIX and earlier UNIX system implementation, this password example is treated as if the user had only entered ``This is '' because only the first 8 characters are used for encryption.
In SCO UNIX systems, Release 3.2, the first 8 characters are still encrypted as before. Any characters following this in the clear-text password are then encrypted in groups of up to 8 characters each, using a salt\*(F derived from the preceding encrypted segment, and added to the end of the encrypted string.
No check is made to verify that the user
exists and is not locked out of the system,
because it is not possible for the user
to execute this code without being logged in.
loge.c (Part 7/17)
154 void 155 failed( 156 int code, /* exit(S) status */ 157 char *who, /* Name of attempting user, if known */ 158 char *argv[] /* Command attempted (with arguments) */ 159 ) { 160 if (who != (char *)0 || (who = cuserid((char *)0)) != (char *)0) 161 sulog(who, FALSE, argv); 162 exit(code); 163 /* NOTREACHED */ 164 }
165 void log_write(FILE *, char *, char *, struct tm *, int, char **); 166 FILE *log_open(char *);
167 void 168 sulog( 169 char *who, 170 int success, 171 char *argv[] 172 ) { 173 char *logf; /* Name of su logfile */ 174 char *ctty; /* Name of console */ 175 char *ttyn; /* Name of this tty */ 176 time_t now; /* Current time (secs) */ 177 struct tm at; 178 FILE *fp; 179 char *str;
180 logf = (char *)0; 181 ctty = (char *)0; 182 if (defopen(su_defaults) == 0) { 183 if ((str = defread("SULOG=")) != (char *)0) 184 logf = strdup(str); 185 if ((str = defread("CONSOLE=")) != (char *)0) 186 ctty = strdup(str); 187 defopen((char *)0); /*close the defaults file */ 188 } 189 if (logf == (char *)0 && ctty == (char *)0) 190 return;
loge.c (Part 8/17)
191 if ((ttyn = ttyname(0)) == (char *)0) 192 if ((ttyn = ttyname(1)) == (char *)0) 193 if ((ttyn = ttyname(2)) == (char *)0) 194 ttyn = "/dev/tty??";
195 now = time((time_t *)0); 196 at = *localtime(&now);
197 if (logf != (char *)0 && (fp = log_open(logf)) != (FILE *)0) { 198 log_write(fp, who, ttyn, &at, success, argv); 199 (void) fclose(fp); 200 }
201 if (ctty != (char *)0 && strcmp(ctty, ttyn) != 0 && 202 (fp = fopen(ctty, "a")) != (FILE *)0 203 ) { 204 log_write(fp, who, ttyn, &at, success, argv); 205 (void) fclose(fp); 206 } 207 }
loge.c (Part 9/17)
208 void graphic(char *, FILE *);209 void 210 log_write( 211 FILE *fp, /* Log file (opened to append) */ 212 char *who, /* Name of user responsible */ 213 char *where, /* Location (terminal) */ 214 struct tm *when, /* Date and time */ 215 int success, /* TRUE if command will be run */ 216 char *argv[] /* Command (and arguments) run */ 217 ) { 218 char *devn; /* Basename of terminal <where> */
219 if ((devn = strrchr(where, '/')) != (char *)0) 220 devn++; 221 else 222 devn = where; 223 (void) fprintf(fp, "LOGE %.2d/%.2d %.2d:%.2d %c %s %s-root", 224 when->tm_mon+1, when->tm_mday, when->tm_hour, when->tm_min, 225 success ? '+' : '-', devn, who 226 ); 227 while (*argv != (char *)0) { 228 putc(' ', fp); 229 graphic(*argv, fp); 230 argv++; 231 } 232 putc('\n', fp); 233 }
loge.c (Part 10/17)
234 void 235 graphic( 236 char *s, 237 FILE *fp 238 ) { 239 char c;
240 while ((c = *s++) != '\0') { 241 if ( ! isascii(c)) { 242 (void) fputs("M-", fp); 243 c = toascii(c); 244 } 245 if (c == 0177) { 246 putc('^', fp); 247 c = '?'; 248 } 249 else if (iscntrl(c)) { 250 putc('^', fp); 251 c |= 0100; 252 } 253 putc(c, fp); 254 } 255 }
loge.c (Part 11/17)
256 char *cfs_to_str(int);257 FILE * 258 log_open( 259 char *logfile 260 ) { 261 int dei; /* Database Error Indication */
262 if (eaccess(logfile, F_OK)) { 263 dei = create_file_securely(logfile, AUTH_VERBOSE, 264 "record attempt to run program as superuser" 265 ); 266 if (dei != CFS_GOOD_RETURN) { 267 (void) fprintf(stderr, "%s: %s: %s", 268 command_name, cfs_to_str(dei), logfile 269 ); 270 return ((FILE *)0); 271 } 272 } 273 return (fopen(logfile, "a")); 274 }
275 struct namepair cfs_str_tab[] = { 276 "Successfully and securely created file", CFS_GOOD_RETURN, 277 "Cannot securely create new file", CFS_CAN_NOT_OPEN_FILE, 278 "File not listed in File Control database", CFS_NO_FILE_CONTROL_ENTRY, 279 "Cannot change mode on newly created file", CFS_CAN_NOT_CHG_MODE, 280 "Cannot set owner of newly created file", CFS_CAN_NOT_CHG_OWNER_GROUP, 281 (char *)0, 0 282 };
283 char * 284 cfs_to_str( 285 int code /* Return value from create_file_securely() */ 286 ) { 287 struct namepair *np;
288 for (np = cfs_str_tab; np->name != (char *)0; np++) 289 if (np->value == code) 290 return (np->name); 291 return ("Unknown problem with secure file creation"); 292 }
() return value
to a message.
loge.c (Part 12/17)
293 void 294 syserror( 295 int code, 296 char *fmt, 297 ... 298 ) { 299 va_list args;
300 (void) fprintf(stderr, "%s: ", command_name);
301 va_start(args, fmt); 302 (void) vfprintf(stderr, fmt, args); 303 va_end(args);
304 (void) fputs(": ", stderr); 305 if (0 < code && code < sys_nerr) 306 (void) fprintf(stderr, "%s (error %d)", sys_errlist[code], code); 307 else 308 (void) fprintf(stderr, "Unknown system error %d", code); 309 putc('\n', stderr); 310 }
loge.c (Part 13/17)
311 int
312 check_TCB_path(
313 	char *path,
314 	char ptype,
315 	char *desc
316 ) {
317 	int result;
318 	if (check_DAC_path(path, ptype, desc))
319 		result = -1;
320 	else if ((path = find_auth_file((char *)0, OT_FILE_CNTL)) == (char *)0)
321 		result = -1;
322 	else {
323 		result = check_DAC_path(path, TCB_FILE, "File Control database");
324 		free(path);
325 	}
326 	return (result);
327 }
loge.c (Part 14/17)
328 int
329 check_DAC_path(
330 	char *path,
331 	char ptype,
332 	char *desc
333 ) {
334 	char c, *temp, *cp, *text, buf[ARBSTRSIZ], type;
335 	struct pr_file *prf;
336 	enum DAC_status sts;
337 	if ((temp = strdup(path)) == (char *)0) {
338 		audit_no_resource(path, OT_MEMORY, "cannot check pathname");
339 		return (-1);
340 	}
341 	cp = temp + 1;		/* Assume absolute path (*temp is "/")	*/
342 	(void) sprintf(buf, "Path leading to %s", desc);
343 	text = buf;
344 	type = TCB_DIRECTORY;
345 	for (;;) {
346 		if (cp == (char *)0 || (c = *cp) == ' ') {
347 			text = desc;
348 			type = ptype;
349 		}
350 		else {
351 			c = *cp;
352 			*cp = ' ';
353 		}
354 		if ((prf = getprfinam(temp)) == (struct pr_file *)0)
355 			sts = NotListed;
356 		else if (type && prf->uflg.fg_type && *prf->ufld.fd_type != type)
357 			sts = WrongDAC;
358 		else
359 			sts = compare_DAC_attributes(temp, prf);
loge.c (Part 15/17)
360 switch (sts) { 361 case MatchesDAC: 362 case ExceedsDAC: 363 break; 364 case NotListed: 365 sa_audit_security_failure( 366 OT_FILE_CNTL, 367 (long)NotListed, (long)MatchesDAC, 368 temp, text 369 ); 370 free(temp); 371 return (-1); 372 case WrongDAC: 373 default: 374 audit_lax_file(temp, text); 375 free(temp); 376 return (-1); 377 } 378 if (cp == (char *)0 || (*cp = c) == '\0') 379 break; 380 cp = strchr(cp + 1, '/'); 381 } 382 free(temp);
383 return (0); 384 }
loge.c (Part 16/17)
385 enum DAC_status
386 compare_DAC_attributes(
387 	char *path,
388 	register struct pr_file *prf
389 ) {
390 	struct stat stbuf;
391 	mode_t f_mode, d_mode;
392 	char f_type;
393 	if (prf == (struct pr_file *)0)
394 		return (NotListed);
395 	if (stat(path, &stbuf))
396 		return (NoSuchFile);
397 	if (prf->uflg.fg_uid && prf->ufld.fd_uid != stbuf.st_uid)
398 		return (WrongDAC);
399 	if (prf->uflg.fg_gid && prf->ufld.fd_gid != stbuf.st_gid)
400 		return (WrongDAC);
401 	if (prf->uflg.fg_type) {
402 		switch (stbuf.st_mode & S_IFMT) {
403 		    case S_IFDIR:	f_type = 'd';	break;
404 		    case S_IFREG:	f_type = 'r';	break;
405 		    case S_IFCHR:	f_type = 'c';	break;
406 		    case S_IFBLK:	f_type = 'b';	break;
407 #ifdef S_IFIFO
408 		    case S_IFIFO:	f_type = 'f';	break;
409 #endif
410 #ifdef S_IFSOCK
411 		    case S_IFSOCK:	f_type = 's';	break;
412 #endif
As a general rule, everything listed in the File Control database is part of the TCB.
loge.c (Part 17/17)
413 #ifdef S_IFLNK 414 case S_IFLNK: f_type = 'l'; break; 415 #endif 416 default: return (WrongDAC); 417 } 418 if (*prf->ufld.fd_type != f_type) 419 return (WrongDAC); 420 }421 f_mode = (stbuf.st_mode & ~S_IFMT); 422 if (prf->uflg.fg_mode) 423 d_mode = (prf->ufld.fd_mode & ~S_IFMT); 424 else 425 d_mode = 0; 426 if (d_mode == f_mode) 427 return (MatchesDAC); 428 else if ((~d_mode & f_mode) == 0) 429 return (ExceedsDAC);
430 return (WrongDAC); 431 }