Carleton University School of Computer Science
COMP 3000 (WINTER 2021) OPERATING SYSTEMS ASSIGNMENT 2 (SOLUTIONS)
Questions – part 1 [6]
1. [3] In the context of this course when you write code in C, list three ways a program can get the value of an environment variable. Be specific but concise.
–
– The variable (i.e., )
– The third argument of )
getenv()
environ
main()
extern char **environ;
(i.e.,
char *envp[]
2. [3] Mention two obvious reasons why the password of a user (e.g., student in our course VM) cannot be easily retrieved by unauthorized parties. Assume that the unauthorized party already has access to your VM, as another non-root user.
Also note: you should not assume non-root users can easily get the root privilege using the command sudo. In our case, it’s just configured to facilitate operations, otherwise a user is usually not supposed to be able to .
– The permission bits of is -rw-r—–, which means only root or users from the root group can read it.
– The password stored in /etc/shadow is not plaintext but shadowed (hashed).
(advanced) A cryptographic hash function is one-way, i.e., it’s computationally infeasible to reverse it and get the plaintext.
sudo root
/etc/shadow
Questions – part 2 [12]
Download the original 3000userlogin.c in Tutorial 4:
1. Beforeansweringanyquestionsbelow,firstpatchtheoriginalfilewiththediffhere.Inthedirectory where 3000userlogin.c is located:
patch –ignore-whitespace <3000userlogin-passwd.diff
This makes your 3000userlogin capable of prompting for a password and verifying it. There are no marks assigned to this step. As your original 3000userlogin.c has been replaced, to be clear, you can now rename it to 3000userlogin-patched.c. Use the patched file for all the following questions. Compile and run it as you did with the original 3000userlogin. Download directly: patched version.
2. [2]Whyisline38(the line)needed?Orputanotherway,whatwillhappenwithoutit?
The function modifies the string passed to it on each call (which is kind of convenient for a sequence of calls, but not intuitive though).
Otherwise, you no longer have the original string to compare with. Depending on the
strtok()
memcpy()
compared length, either the authentication will always succeed (if comparing the remaining string) or always fail (if comparing the whole, due to different sizes).
3. [2]Sometimes,youneedtoinvokeanothercommand-lineprogramforcertainpurposes.Inthe patched 3000userlogin, openssl is needed for computing the password hash. Check the man page of the function. Mention two reasons as to why exec() cannot be used for this purpose.
- forks a new process, very important, as otherwise the current program will be overwritten (no way to come back).
- can create a pipe (I/O stream) to the standard output of the called program
( , which is needed here and does not do.
popen()
openssl)
popen()
popen()
exec()
4. [2]Aswepopen()theopensslcommandonlyforreading(“r”)itsstandardoutput,whathappens to its standard input [1/2]? How do you know it [1/2]?
The standard input is shared with (or the same as) that of 3000userlogin-patched. Checking the man page of popen(), it says “Conversely, reading from the stream reads the command's standard output, and command's standard input is the same as that of the process that called popen().” Especially, you are able to type the password, at the current terminal, to the popened program, which means the standard input is still shared. (recall the shared file descriptors issue with 3000shell)
5. [2]InTutorial4,ifyoucompilethecodewithoutconfiguringsetuidroot,youcanatleastlogin with your current username (e.g., student), which has been well explained. However, for the patched 3000userlogin, without , you cannot log in as any user, always with the error
setuid root
message “Could not find user”. Why does it happen [1/2]? How did you find it out [1/2]?
The original reads only
- (readable by everyone).
-), which is not accessible to regular users. You may try removing or inserting
failing, or by reading the man page of
(for account info), which has now also reads /etc/shadow (
s around and find out where it’s you can tell is involved.
3000userlogin
/etc/passwd
-rw-r--r-
3000userlogin-patched
-rw-r----
printf()
getspnam()
getspnam()
/etc/shadow
6. [1]Whycan’tyouusefclose()toclosethehandlefponline49,insteadofpclose()?(donotjust
say because it was created with , not
Corresponding to what does,
before closing the stream (in addition to what
a zombie process or undefined behavior.
Additional note: what enters the zombie state (defunct) will not be openssl (which is taken care of by sh), but sh called by popen().
7. [3]Since3000userloginresemblesthecommandsuinsomesense,nextyoucanmake 3000userlogin-patched’s behavior similar to that of the command sudo:
- Take a second argument (next to the username) as the command to execute.
- All subsequent arguments (if any) should be passed verbatim to the command.
- After successful authentication with the password, the command should run normally.
student@compxxxx:~$ ./3000userlogin-patched someuser ls -l Password:
<<
student@compxxxx:~$ ./3000userlogin-patched someuser whoami Password:
someuser
student@compxxxx:~$
– Note: you should not need to provide the full path. You can just use ls instead of .
popen()
fopen()
)
popen()
pclose()
waits for the called process to terminate does). Using fclose() alone will leave
fclose()
/bin/ls
– Once finished, you should be back to where 3000userlogin-patched was invoked, as with sudo. Hint: consider using a better function than the current execve(). Don’t bother to do what was done in 3000shell, i.e., avoiding using find_binary().
See the code here for your reference.
8. BONUS[4]Change3000userlogin-patchedsothatwhennousernameisgiven,theprogram prompts for a username by displaying “Username:”
a. Important: what is typed by the user must NOT be visible, as with “Password:” (echo turned off). b. The behavior of the rest of the program should NOT be affected, i.e., it proceeds as if the
2
username was provided as an argument, prompts for the password for verification as before. After logged in, things should work as before.
Achieving both a and b will get 4 bonus marks (no partial marks).
See the code here for your reference.
3