int check_ipc (uid_t uid, gid_t gid) { FILE *fd; char *ret_char; char readbuf[80]; char ipcscommand[80]; char ipc_buf[10]; int ipc_id; char ipc_obj[3] = { 'm', 's', 'q' }; /* m => SHM, s => SEM, Q => MSQ */ int opt; setids (0, 0); for (opt = 0; opt < 3; opt++) { /* Store the resulto of ipcscommand in tmpfile and read each * line to test IPC objects */ memset (ipcscommand, '\0', sizeof (ipcscommand)); sprintf (ipcscommand, "ipcs -%c >%s", ipc_obj[opt], tmpfile); system (ipcscommand); fd = fopen (tmpfile, "r"); /* Skip the header */ fgets (readbuf, sizeof (readbuf), fd); fgets (readbuf, sizeof (readbuf), fd); fgets (readbuf, sizeof (readbuf), fd); memset (readbuf, '\0', sizeof (readbuf)); ret_char = fgets (readbuf, sizeof (readbuf), fd); while ((ret_char != NULL) && (*readbuf != '\n')) { sscanf (readbuf, "%s %d", ipc_buf, &ipc_id); /* ipc_id stores the id of ipc object */ testall_ipc (ipc_id, ipc_obj[opt], uid, gid); memset (readbuf, '\0', sizeof (readbuf)); ret_char = fgets (readbuf, sizeof (readbuf), fd); } fclose (fd); unlink (tmpfile); } return (0); }
int main(int argc,char *argv[]) { register int m,n; register char *p; struct stat statx; int mode; uid_t effuid; gid_t effgid; NOT_USED(argc); arglist = argv; if((command = argv[1]) == 0) error_exit(badexec); ruserid = getuid(); euserid = geteuid(); rgroupid = getgid(); egroupid = getegid(); p = argv[0]; #ifndef _lib_setreuid maketemp(tmpname); if(strcmp(p,tmpname)==0) { /* At this point, the presumption is that we are the * version of THISPROG copied into /tmp, with the owner, * group, and setuid/gid bits correctly set. This copy of * the program is executable by anyone, so we must be careful * not to allow just any invocation of it to succeed, since * it is setuid/gid. Validate the proper execution by * examining the FDVERIFY file descriptor -- if it is owned * by root and is mode SPECIAL, then this is proof that it was * passed by a program with superuser privileges -- hence we * can presume legitimacy. Otherwise, bail out, as we suspect * an impostor. */ if(fstat(FDVERIFY,&statb) < 0 || statb.st_uid != 0 || (statb.st_mode & ~S_IFMT) != SPECIAL || close(FDVERIFY)<0) error_exit(badexec); /* This enables the grandchild to clean up /tmp file */ close(FDSYNC); /* Make sure that this is a valid invocation of the clone. * Perhaps unnecessary, given FDVERIFY, but what the heck... */ if(stat(tmpname,&statb) < 0 || statb.st_nlink != 1 || !S_ISREG(statb.st_mode)) error_exit(badexec); if(ruserid != euserid && ((statb.st_mode & S_ISUID) == 0 || statb.st_uid != euserid)) error_exit(badexec); goto exec; } /* Make sure that this is the real setuid program, not the clone. * It is possible by clever hacking to get past this point in the * clone, but it doesn't do the hacker any good that I can see. */ if(euserid) error_exit(badexec); #endif /* _lib_setreuid */ /* Open the script for reading first and then validate it. This * prevents someone from pulling a switcheroo while we are validating. */ n = open(p,0); if(n == FDIN) { n = dup(n); close(FDIN); } if(n < 0) error_exit(badopen); /* validate execution rights to this script */ if(fstat(FDIN,&statb) < 0 || (statb.st_mode & ~S_IFMT) != SPECIAL) euserid = ruserid; else euserid = statb.st_uid; /* do it the easy way if you can */ if(euserid == ruserid && egroupid == rgroupid) { if(access(p,X_OK) < 0) error_exit(badexec); } else { /* have to check access on each component */ while(*p++) { if(*p == '/' || *p == 0) { m = *p; *p = 0; if(eaccess(argv[0],X_OK) < 0) error_exit(badexec); *p = m; } } p = argv[0]; } if(fstat(n, &statb) < 0 || !S_ISREG(statb.st_mode)) error_exit(badopen); if(stat(p, &statx) < 0 || statb.st_ino != statx.st_ino || statb.st_dev != statx.st_dev) error_exit(badexec); if(stat(THISPROG, &statx) < 0 || (statb.st_ino == statx.st_ino && statb.st_dev == statx.st_dev)) error_exit(badexec); close(FDIN); if(fcntl(n,F_DUPFD,FDIN) != FDIN) error_exit(badexec); close(n); /* compute the desired new effective user and group id */ effuid = euserid; effgid = egroupid; mode = 0; if(statb.st_mode & S_ISUID) effuid = statb.st_uid; if(statb.st_mode & S_ISGID) effgid = statb.st_gid; /* see if group needs setting */ if(effgid != egroupid) if(effgid != rgroupid || setgid(rgroupid) < 0) mode = S_ISGID; /* now see if the uid needs setting */ if(mode) { if(effuid != ruserid) mode |= S_ISUID; } else if(effuid) { if(effuid != ruserid || setuid(ruserid) < 0) mode = S_ISUID; } if(mode) setids(mode, effuid, effgid); #ifndef _lib_setreuid exec: #endif /* _lib_setreuid */ /* only use SHELL if file is in trusted directory and ends in sh */ shell = getenv("SHELL"); if(shell == 0 || !endsh(shell) || ( !in_dir("/bin",shell) && !in_dir("/usr/bin",shell) && !in_dir("/usr/lbin",shell) && !in_dir("/usr/local/bin",shell))) shell = DEFSHELL; argv[0] = command; argv[1] = (char*)devfd; execv(shell,argv); error_exit(badexec); }
/* * Test access for owner, group, other */ void testall (struct stat *ostatbufp, char *pathname, uid_t uid, gid_t gid) { int i; int rc = 0; char outbuf[512]; struct passwd passwd; struct passwd *passwdp; struct group group; struct group *groupp; struct stat statbuf; struct stat *statbufp = &statbuf; char *pbuf; char *gbuf; passwdp = (struct passwd *) malloc (sizeof (passwd)); groupp = (struct group *) malloc (sizeof (group)); pbuf = (char *) malloc (4096); gbuf = (char *) malloc (4096); memset (pbuf, '\0', 4096); memset (gbuf, '\0', 4096); setids (0, 0); printf ("\n%s\n", pathname); /* For test 1 we chown the file owner/group */ if (test == 1) { if ((rc = chown (pathname, uid, gid)) == -1) { printf ("ERROR: unable to chown %s to %d:%d\n", pathname, uid, gid); goto EXIT; } } /* Start with clean buffers */ memset (&statbuf, '\0', sizeof (statbuf)); /* Get file stat info to determine actual owner and group */ stat (pathname, &statbuf); /* * If we successfully chown'd the file, but the owner hasn't changed * log it and skip. */ if ((test == 1) && ((statbufp->st_uid != uid) || (statbufp->st_gid != gid))) { printf ("INFO: chown success, but file owner " "did not change: %s\n", pathname); goto EXIT; } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "MODE: "); for (i = 0; i < sizeof (perms) / sizeof (int); i++) { if (statbufp->st_mode & perms[i]) { strcat (outbuf, ptext[i]); } else { strcat (outbuf, "-"); } } getpwuid_r (statbufp->st_uid, &passwd, pbuf, 4096, &passwdp); getgrgid_r (statbufp->st_gid, &group, gbuf, 4096, &groupp); sprintf (&(outbuf[strlen (outbuf)]), "%s:%s\n", passwd.pw_name, group.gr_name); printf ("%s", outbuf); /* Check owner access for read/write */ setids (statbufp->st_uid, gid_nobody); memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Owner read\t"); /* * If we are root, we expect to succeed event * without explicit permission. */ if ((statbufp->st_mode & S_IRUSR) || (statbufp->st_uid == 0)) { testaccess (pathname, O_RDONLY, 0, outbuf); } else { testaccess (pathname, O_RDONLY, -1, outbuf); } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Owner write\t"); /* * If we are root, we expect to succeed event * without explicit permission. */ if ((statbufp->st_mode & S_IWUSR) || (statbufp->st_uid == 0)) { testaccess (pathname, O_WRONLY, 0, outbuf); } else { testaccess (pathname, O_WRONLY, -1, outbuf); } /* Check group access for read/write */ setids (0, 0); setids (uid_nobody, statbufp->st_gid); memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Group read\t"); if (statbufp->st_mode & S_IRGRP) { testaccess (pathname, O_RDONLY, 0, outbuf); } else { testaccess (pathname, O_RDONLY, -1, outbuf); } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Group write\t"); if (statbufp->st_mode & S_IWGRP) { testaccess (pathname, O_WRONLY, 0, outbuf); } else { testaccess (pathname, O_WRONLY, -1, outbuf); } /* Check other access for read/write */ setids (0, 0); setids (uid_nobody, gid_nobody); memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Other read\t"); if (statbufp->st_gid == gid_nobody) { /* special case! file's gid == our 'other' gid */ if (statbufp->st_mode & S_IRGRP) { testaccess (pathname, O_RDONLY, 0, outbuf); } else { testaccess (pathname, O_RDONLY, -1, outbuf); } } else { if (statbufp->st_mode & S_IROTH) { testaccess (pathname, O_RDONLY, 0, outbuf); } else { testaccess (pathname, O_RDONLY, -1, outbuf); } } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Other write\t"); if (statbufp->st_gid == gid_nobody) { /* special case! ile's gid == our 'other' gid */ if (statbufp->st_mode & S_IWGRP) { testaccess (pathname, O_WRONLY, 0, outbuf); } else { testaccess (pathname, O_WRONLY, -1, outbuf); } } else { if (statbufp->st_mode & S_IWOTH) { testaccess (pathname, O_WRONLY, 0, outbuf); } else { testaccess (pathname, O_WRONLY, -1, outbuf); } } setids (0, 0); if (test == 1) { chown (pathname, ostatbufp->st_uid, ostatbufp->st_gid); } EXIT: return; }
int testall_ipc (int ipc_id, char opt, uid_t uid, gid_t gid) { int rc = 0; char outbuf[256]; struct passwd passwd; struct passwd *passwdp; struct group group; struct group *groupp; struct shmid_ds shbuf; struct semid_ds sembuf; struct msqid_ds msqbuf; struct ipc_perm *ipcperm; char *pbuf; char *gbuf; uid_t tmpuid; gid_t tmpgid; pbuf = (char *) malloc (4096); gbuf = (char *) malloc (4096); memset (pbuf, '\0', 4096); memset (gbuf, '\0', 4096); setids (0, 0); printf ("\nIPC ID: %d - %s\n", ipc_id, ((opt == 's') ? "Semaphore Sets" : (opt == 'm') ? "Shared Memory Segment" : "Message Queues")); switch (opt) { /* Shared Memory */ case 'm': /* Get stat information of shared memory. */ if ((rc = shmctl (ipc_id, IPC_STAT, &shbuf)) == -1) { printf ("Error getting stat of Shared Memory: %d\n", errno); return (rc); } else { /* ipcperm get the shm_perm memory address to manipulate * permissions with the same struct (ipc_perm). */ ipcperm = &(shbuf.shm_perm); } break; /* Semaphores */ case 's': /* Set permission mode for semaphore. */ if ((rc = semctl (ipc_id, 0, IPC_STAT, &sembuf)) == -1) { printf ("Error getting stat of Semaphores: %d\n", errno); return (rc); } else { /* ipcperm get the sem_perm memory address to manipulate * permissions with the same struct (ipc_perm). */ ipcperm = &(sembuf.sem_perm); } break; /* Message Queues */ case 'q': if ((rc = msgctl (ipc_id, IPC_STAT, &msqbuf)) == -1) { printf ("Error getting stat of Message Queue: %d\n", errno); return (rc); } else { /* ipcperm get the msg_perm memory address to manipulate * permissions with the same struct (ipc_perm). */ ipcperm = &(msqbuf.msg_perm); } break; } /* For test 1, change owner and group */ if (test == 1) { tmpuid = ipcperm->uid; tmpgid = ipcperm->gid; ipcperm->uid = uid; ipcperm->gid = gid; switch (opt) { /* Shared Memory */ case 'm': if ((rc = shmctl (ipc_id, IPC_SET, &shbuf)) == -1) { printf ("Error setting stat of Shared Memory: " "%d\n", errno); return (rc); } break; /* Semaphores */ case 's': if ((rc = semctl (ipc_id, 0, IPC_SET, &sembuf)) == -1) { printf ("Error setting stat of Semaphores: " "%d\n", errno); return (rc); } break; /* Message Queues */ case 'q': if ((rc = msgctl (ipc_id, IPC_SET, &msqbuf)) == -1) { printf ("Error setting stat of Message Queue: " "%d\n", errno); return (rc); } break; } } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "MODE: "); strcat (outbuf, (((ipcperm->mode & S_IRUSR) != 0) ? "r" : "-")); strcat (outbuf, (((ipcperm->mode & S_IWUSR) != 0) ? "w" : "-")); strcat (outbuf, "-"); strcat (outbuf, (((ipcperm->mode & S_IRGRP) != 0) ? "r" : "-")); strcat (outbuf, (((ipcperm->mode & S_IWGRP) != 0) ? "w" : "-")); strcat (outbuf, "-"); strcat (outbuf, (((ipcperm->mode & S_IROTH) != 0) ? "r" : "-")); strcat (outbuf, (((ipcperm->mode & S_IWOTH) != 0) ? "w" : "-")); strcat (outbuf, "-"); getpwuid_r (ipcperm->uid, &passwd, pbuf, 4096, &passwdp); getgrgid_r (ipcperm->gid, &group, gbuf, 4096, &groupp); /* Print owner:group */ sprintf (&(outbuf[strlen (outbuf)]), " %s:%s\n", passwd.pw_name, group.gr_name); /* Change current owner of process to access IPC function */ setids (ipcperm->uid, gid_nobody); strcat (outbuf, "Owner read\t"); /* * If we are root, we expect to succeed event * without explicit permission. */ if ((ipcperm->mode & S_IRUSR) || (ipcperm->uid == 0)) { testaccess_ipc (ipc_id, opt, O_RDONLY, 0, outbuf); } else { testaccess_ipc (ipc_id, opt, O_RDONLY, -1, outbuf); } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Owner write\t"); /* * If we are root, we expect to succeed event * without explicit permission. */ if ((ipcperm->mode & S_IWUSR) || (ipcperm->uid == 0)) { testaccess_ipc (ipc_id, opt, O_WRONLY, 0, outbuf); } else { testaccess_ipc (ipc_id, opt, O_WRONLY, -1, outbuf); } /* Check group access for read/write */ setids (0, 0); setids (uid_nobody, ipcperm->gid); memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Group read\t"); if (ipcperm->mode & S_IRGRP) { testaccess_ipc (ipc_id, opt, O_RDONLY, 0, outbuf); } else { testaccess_ipc (ipc_id, opt, O_RDONLY, -1, outbuf); } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Group write\t"); if (ipcperm->mode & S_IWGRP) { testaccess_ipc (ipc_id, opt, O_WRONLY, 0, outbuf); } else { testaccess_ipc (ipc_id, opt, O_WRONLY, -1, outbuf); } /* Check other access for read/write */ setids (0, 0); setids (uid_nobody, gid_nobody); memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Other read\t"); if (ipcperm->mode & S_IROTH) { testaccess_ipc (ipc_id, opt, O_RDONLY, 0, outbuf); } else { testaccess_ipc (ipc_id, opt, O_RDONLY, -1, outbuf); } memset (outbuf, '\0', sizeof (outbuf)); strcat (outbuf, "Other write\t"); if (ipcperm->mode & S_IWOTH) { testaccess_ipc (ipc_id, opt, O_WRONLY, 0, outbuf); } else { testaccess_ipc (ipc_id, opt, O_WRONLY, -1, outbuf); } setids (0, 0); /* Back to the original owner */ if (test == 1) { ipcperm->uid = tmpuid; ipcperm->gid = tmpgid; switch (opt) { /* Shared Memory */ case 'm': if ((rc = shmctl (ipc_id, IPC_SET, &shbuf)) == -1) { printf ("Error setting stat back of Shared " "Memory: %d\n", errno); return (rc); } break; /* Semaphores */ case 's': if ((rc = semctl (ipc_id, 0, IPC_SET, &sembuf)) == -1) { printf ("Error setting stat back of Semaphores: " "%d\n", errno); return (rc); } break; /* Message Queues */ case 'q': if ((rc = msgctl (ipc_id, IPC_SET, &msqbuf)) == -1) { printf ("Error setting stat back of Message " "Queue: %d\n", errno); return (rc); } break; } } return (0); }
void testaccess_ipc (int ipc_id, char opt, int mode, int expected, char *outbuf) { int actual, semval, rc; int myerror = 0; char *chPtr; struct sembuf sop; uid_t tmpuid; gid_t tmpgid; struct msqbuf { long mtype; char mtext[80]; } s_message, r_message; /* If we are root, we expect to succeed event * without explicit permission. */ strcat (outbuf, (expected == -1) ? "expected: fail " : "expected: pass "); switch (opt) { /* Shared Memory */ case 'm': /* Try to get (mode) access * There is no notion of a write-only shared memory * segment. We are testing READ ONLY and READWRITE access. */ chPtr = shmat (ipc_id, NULL, (mode == O_RDONLY) ? SHM_RDONLY : 0); if (chPtr != (void *) -1) { strcat (outbuf, "actual: pass "); actual = 0; if (shmdt (chPtr) == -1) { perror ("Warning: Could not dettach memory segment"); } } else { myerror = errno; strcat (outbuf, "actual: fail "); actual = -1; } break; /* Semaphores */ case 's': tmpuid = geteuid (); tmpgid = getegid (); semval = semctl (ipc_id, 0, GETVAL); /* Need semaphore value == 0 to execute read permission test */ if ((mode == O_RDONLY) && (semval > 0)) { setids (0, 0); if ((semctl (ipc_id, 0, SETVAL, 0)) == -1) { printf ("Unable to set semaphore value: %d\n", errno); } setids (tmpuid, tmpgid); } /* Try to get mode access */ sop.sem_num = 0; sop.sem_op = mode; sop.sem_flg = SEM_UNDO; actual = semop (ipc_id, &sop, 1); myerror = errno; if (actual != -1) { strcat (outbuf, "actual: pass "); /* back to semaphore original value */ if (mode != O_RDONLY) { sop.sem_op = -1;/* decrement semaphore */ rc = semop (ipc_id, &sop, 1); } } else { /* Back to semaphore original value */ if ((mode == O_RDONLY) && (semval > 0)) { setids (0, 0); if ((semctl (ipc_id, 0, SETVAL, semval)) == -1) { printf ("Unable to set semaphore " "value: %d\n", errno); } setids (tmpuid, tmpgid); } strcat (outbuf, "actual: fail "); } break; /* Message Queues */ case 'q': tmpuid = geteuid (); tmpgid = getegid (); if (mode == O_RDONLY) { setids (0, 0); /* Send a message to test msgrcv function */ s_message.mtype = 1; memset (s_message.mtext, '\0', sizeof (s_message.mtext)); strcpy (s_message.mtext, "First Message\0"); if ((rc = msgsnd (ipc_id, &s_message, strlen (s_message.mtext), 0)) == -1) { printf ("Error sending first message: %d\n", errno); } setids (tmpuid, tmpgid); } s_message.mtype = 1; memset (s_message.mtext, '\0', sizeof (s_message.mtext)); strcpy (s_message.mtext, "Write Test\0"); /* Try to get WRITE access */ if (mode == O_WRONLY) { actual = msgsnd (ipc_id, &s_message, strlen (s_message.mtext), 0); } else { /* Try to get READ access */ actual = msgrcv (ipc_id, &r_message, sizeof (r_message.mtext), 0, IPC_NOWAIT); } myerror = errno; if (actual != -1) { strcat (outbuf, "actual: pass "); } else { strcat (outbuf, "actual: fail "); } if (((mode == O_RDONLY) && (actual == -1)) || ((mode == O_WRONLY) && (actual != -1))) { setids (0, 0); /* discard the message send */ rc = msgrcv (ipc_id, &r_message, sizeof (r_message.mtext), 0, IPC_NOWAIT); setids (tmpuid, tmpgid); } break; } if ((actual == expected) || ((expected == 0) && (actual != -1))) { strcat (outbuf, "\tresult: PASS\n"); totalpass++; } else { errno = myerror;// restore errno from correct error code sprintf (&(outbuf[strlen (outbuf)]), "\tresult: FAIL : " "errno = %d\n", errno); totalfail++; } printf ("%s", outbuf); return; }