System Programming Chapter 8.11 1 Fun Stuff • Touch-based Communication 2 Changing User/Group ID’s • This part is confusing … (when I read it) • Why changes user id in the middle of a program execution? – Least-privilege model: a program should use the least privilege necessary to accomplish any task. – Reduce window of security vulnerability – Example: a process needs to gain a privilege to access a privileged file 3 Recall … • A process can have more than one ID. – Real user/group ID: who you really are – Effective user/group ID: determine file access permission – Supplementary group IDs – Saved set-user/group-ID = owner of the program with set-user-id bit set • Why do you need saved set-user-id? – cannot give up privilege temporarily (and get it back) 4 Recall … #include <sys/types.h> #include <unistd.h> int setuid(uid_t uid); – – – – The process == superuser set real/effective/saved-suid = uid Otherwise, euid=uid if uid == ruid or uid == saved-suid (suid = set-uid) Return 0 upon success Return -1 otherwise; errno=EPERM (_POSIX_SAVED_IDS) int setgid(gid_t gid); – The same as setuid 5 User/Group ID’s • Only superuser process can change the real uid – normally done by the login program. • The euid is set by exec only if the setuid bit is set for the program file. euid can only be set as its saved-suid or ruid. • exec copies the euid to the saved-suid (after the setting of euid if setuid bit is on). 6 User/Group ID’s • Example man program – man is used for displaying manual pages – The setuid bit is on for man (owner=man). • For file locking – man calls setuid(ruid) for privileged file access on some configuration file • Correct uid – Switch the euid back to man after it is done with them 7 Example of using setuid() • Step 1: exec tip program: Real user ID = Our user ID Effective user ID = man Saved-set-user ID = man • Step 2: access the required locks • Step 3: setuid(getuid()) to return to normal permission • Step 4: calling setuid(uucpuid) to change the effective user ID • Step 5: release the lock. Real user ID = Our user ID Effective user ID = Our user ID Saved-set-user ID = man Real user ID = Our user ID Effective user ID = Man Saved-set-user ID = man 8 User/Group ID’s #include <sys/types.h> #include <unistd.h> int setreuid(uid_t ruid, uid_t euid); int setregid(uid_t rgid, uid_t egid); – Change both ruid/rgid and euid/egid. – Swapping of real and effective uids. • Good for even unprivileged users. – BSD only or BSD-compatibility library 9 User/Group ID’s #include <sys/types.h> #include <unistd.h> int seteuid(uid_t uid); int setegid(uid_t gid); – Change only euid/egid. – Non-superusers can only set euid=ruid or saved-setuid. – A privileged user only sets euid = uid. • It is different from setuid(uid) 10 User/Group ID’s superuser setreuid(ruid, euid) euid ruid real uid nonsuperuser setreuid nonsuperuser setuid or seteuid superuser setuid(uid) uid superuser seteuid(uid) uid uid effective uid nonsuperuser setreuid uid saved suid exec of suid nonsuperuser setuid or seteuid • The supplementary guid’s are not affected by the setgid function. 11 Interpreter Files • What is an interpreter? – A program that executes other programs, e.g., java, lisp, python, etc. • Def: a file begins with a line of the form: #! pathname [optional-argument] – E.g., “#! /bin/sh” • Implementation: – Recognition is done within the kernel – Interpreter (normally an absolute pathname) vs the interpreter files – Line-limit of the first line, e.g., 32 chars. • Program 8.10 – Page 218 – Argument list, arg pathname of execl() 12 Examples of Interpreter & Interprted file #include "apue.h" #include <sys/wait.h> int main(void) { pid_t pid; if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { /* child */ if (execl("/home/professor/hchu/sys_prog_0 6/test/testinterp", "testinterp", "myarg1", "MY ARG2", (char *)0) < 0) err_sys("execl error"); } if (waitpid(pid, NULL, 0) < 0) /* parent */ err_sys("waitpid error"); exit(0); } linux1:~/sys_prog_06/test> cat testinterp #!/home/professor/hchu/sys_prog_06/test/ech oarg foo linux1:~/sys_prog_06/test> fig8.20.exe argv[0]: /mnt/professor/hchu/sys_prog_06/test/ech oarg argv[1]: foo argv[2]: /home/professor/hchu/sys_prog_06/test/t estinterp argv[3]: myarg1 argv[4]: MY ARG2 what is the pathname of the interpreter? shifting off the argument list 13 Interpreter Files • “awk –f myfile” lets awk read an awk program from “myfile”. • Argument list: awkexample file1 FILE2 f3 (at /usr/local/bin/) #! /bin/awk –f BEGIN { for (i = 0; i < ARGC; i++) printf “ARGV[%d] = %s\n”, i, ARGV[i] exit } linux1:~/sys_prog_06/test> awkexample file1 FILENAME2 f3 ARGV[0] = awk ARGV[1] = file1 ARGV[2] = FILENAME2 ARGV[3] = f3 mapped to awk -f /home/professor/../awkexample file1 FILENAME2 f3 what if awk is replaced by echoarg? 14 Interpreter Files • Example: Removing of “-f” $su Passwd: # mv /bin/awk /bin/awk.save # cp /home/stevens/bin/echoarg /bin/awk # suspend [1] + Stopped su $arkexample file1 FILE2 f3 argv[0]: /bin/awk argv[1]: -f argv[2]: /usr/local/bin/awkexample argv[3]: file1 argv[4]: FILE2 argv[5]: f3 15 Interpreter Files • Why interpreter files? – Pro: • Hide the fact that certain programs are scripts in other languages. – awkexample optional-arguments • Provide efficiency gain (shell vs. awk scripts) – What if awk script is turned into a shell script? exec twice – awk ‘BEGIN { for (i=0); i<ARGC; i++) …. }’ $* • Choices of shells -> specify in #! /bin/tcsh – Against: • Efficiency for users but at the cost of the kernel – Executable files? /bin/sh? /bin/awk? 16 system execute a command string from within a program #include <stdlib.h> int system(const char *cmdstring); – If cmdstring = null, return nonzero only if a command interpreter is available. • Objective: – Convenient in usage • system(“date > file; ls”); • Or write a program: call time, localtime, strftime, write, etc. – ANSI C definition system-dependent • An interface to shells 17 system • Implementation: fork-exec-waitpid int system(const char *cmdstring) /* version without signal handling */ { pid_t pid; int status; if (cmdstring == NULL) return(1); /* always a command processor with UNIX */ if ((pid = fork()) < 0) { status = -1; /* probably out of processes */ } else if (pid == 0) { /* child */ execl("/bin/sh", "sh", "-c", cmdstring, (char *)0); // Why not execute the command directly? _exit(127); /* execl error */ } else { /* parent */ while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { status = -1; /* error other than EINTR from waitpid() */ break; } } 18 } return(status); system • Program 8.23 – Page 224 – Calling system() • Advantage – system() does all the error handling & signal handling. – Q: what will happen if you hits CTRL-C during the execution? • A security hole – Call system from a setuid program • Programs 8.14 & 8.15 – Page 225 • A set[ug]id program should change its uid’s back to the normal after call fork. – Not in current Linux distribution. 19 #include "apue.h" #include <sys/wait.h> int main(void) { int status; if ((status = system("date")) < 0) err_sys("system() error"); pr_exit(status); linux1:~/sys_prog_06/test> fig8.23.exe ¤G 5¤ë 23 16:12:23 CST 2006 normal termination, exit status = 0 sh: nosuchcommand: command not found normal termination, exit status = 127 p92007 pts/0 2006-05-23 14:22 (epson5.epson.com.tw) r93007 pts/1 2006-05-22 17:30 (linux11.csie.ntu.edu.tw) r93007 pts/2 2006-05-22 17:30 (linux11.csie.ntu.edu.tw) normal termination, exit status = 44 if ((status = system("nosuchcommand")) < 0) err_sys("system() error"); pr_exit(status); if ((status = system("who; exit 44")) < 0) err_sys("system() error"); pr_exit(status); exit(0); } 20 system • A security hole – Call system from a setuid program • Programs 8.24 & 8.25 • A set[ug]id program should change its uid’s back to the normal after call fork. – Not in current Linux distribution. 21 // tsys // printuids #include "apue.h" #include "apue.h" int main(int argc, char *argv[]) { int status; int main(void) { printf("real uid = %d, effective uid = %d\n", getuid(), geteuid()); exit(0); } if (argc < 2) err_quit("command-line argument required"); if ((status = system(argv[1])) < 0) err_sys("system() error"); pr_exit(status); exit(0); } what happens if you execute the following command $ tsys printuids real uid = ? effective uid = ? $ su $ chmod u+s tsys $ tsys printuids real uid = ? effective uid = ? 22 Process Accounting • Accounting Information – Kept in the process table whenever a process terminates. – No records for • crashed processes and • abnormal terminated processes – Each accounting record is written into the accounting file in the order of the termination order of processes. • Q: Can we find the starting order from the accounting file? 23 Process Accounting • Non-POSIX standards – SVR4 & 4.3+BSD supported • accton [filename] • /var/adm/pacct or /usr/adm/acct – Linux • #include <unistd.h> int acct(const char *filename); typedef u_short comp_t; struct acct { char ac_flag; /* Figure 8.26 – Page 251 */ char ac_stat; /* termination status (core flag + signal #) */ uid_t ac_uid; gid_t ac_gid; /* real [ug]id */ dev_t ac_tty; /* controlling terminal */ time_t ac_btime; /* staring calendar time (seconds) */ comp_t ac_utime; /* user CPU time (ticks) */ comp_t ac_stime; /* system CPU time (ticks) */ comp_t ac_etime; /* elapsed time (ticks) */ comp_t ac_mem; /* average memory usage */ comp_t ac_io; /* bytes transferred (by r/w) */ comp_t ac_rw; /* blocks read or written */ char ac_comm[8]; /* command name: [8] for SVR4, [10] for 4.3 BSD */ 24 }; Process Accounting • A new record for each process (not each exec) – E.g., A execs B, then B execs C • ac_flag: AFORK is cleared. (cmd=C) • Programs 8.28 & 8.29 – Page 253-254 parent first child sleep(2) exit(2) sleep(4) abort() fork etime=128 Remark: 60 ticks/sec second child fork third child fork etime=274 execl stat=128+6 /usr/bin/dd flag=AFORK sleep(8) exit(0) fourth child fork sleep(6) kill() etime=360 stat=9 25 F = result of a fork, X = killed by a signal, D = process dumped core elapsed time bytes transferred termination status flags accton e= 7, chars = 64, stat = 0: S dd e= 37, chars = 2218888, stat = 0: second child a.out e= 128, chars = 0, stat = 0: parent a.out e= 274, chars = 0, stat = 134: F a.out e= 360, chars = 0, stat = 9: F a.out e= 484, chars = 0, stat = 0: F D X first child X fourth child third child 26 User Identification • How to know the login name of the user who’s running the process? #include <unistd.h> char *getlogin(void); • Fail if the process is not attached to a terminal – daemons – init, … • A user could have a multiple login names – login’s user name with the same user ID. – getpwuid, getpwnam • Function ttyname – utmp • Environment var LOGNAME (set by the login process in 4.3+BSD) – user-space data 27 Process Times #include <sys/times.h> clock_t times(struct tms *buf); – Timestamps: returned value from some arbitrary point in the past. – Subtract two timestamps to compute elapsed time struct tms { clock_t tms_utime; /* user CPU time */ clock_t tms_stime; /* system CPU time */ clock_t tms_cutime; /* user CPU time, terminated child */ clock_t tms_cstime; /* system CPU time terminated child */ • Program 8.30 – Page 234 – clock_t: measurement in seconds or defined in _SC_CLK_TCK 28 static void pr_times(clock_t real, struct tms *tmsstart, struct tms *tmsend) { static long clktck = 0; int main(int argc, char *argv[]) { int i; setbuf(stdout, NULL); for (i = 1; i < argc; i++) do_cmd(argv[i]); line arg */ exit(0); if (clktck == 0) /* fetch clock ticks per second first time */ if ((clktck = sysconf(_SC_CLK_TCK)) < 0) err_sys("sysconf error"); printf(" real: %7.2f\n", real / (double) clktck); printf(" user: %7.2f\n", (tmsend->tms_utime - tmsstart->tms_utime) / (double) clktck); printf(" sys: %7.2f\n", (tmsend->tms_stime - tmsstart->tms_stime) / (double) clktck); printf(" child user: %7.2f\n", (tmsend->tms_cutime - tmsstart->tms_cutime) / (double) clktck); printf(" child sys: %7.2f\n", (tmsend->tms_cstime - tmsstart->tms_cstime) / (double) clktck); /* once for each command- } static void do_cmd(char *cmd) the "cmd" */ { struct tms tmsstart, tmsend; clock_t start, end; int status; /* execute and time printf("\ncommand: %s\n", cmd); if ((start = times(&tmsstart)) == -1) /* starting values */ err_sys("times error"); } if ((status = system(cmd)) < 0) command */ err_sys("system() error"); command: sleep 5 real: 5.01 user: 0.00 sys: 0.00 child user: 0.00 child sys: 0.00 normal termination, exit status = 0 /* execute if ((end = times(&tmsend)) == -1) values */ err_sys("times error"); pr_times(end-start, &tmsstart, &tmsend); pr_exit(status); } /* ending linux1:~/sys_prog_06/test> fig8.30.exe "sleep 5" "date" command: date ¤G 5¤ë 23 18:37:08 CST 2006 real: 0.01 user: 0.00 sys: 0.00 29