|
|
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 }