Due: Friday, 22 April, 2016 by 11:59:59 PM. Late submissions will be accepted until Tuesday, 26 April without penalty. The absolute last day to submit (with a 30% penalty) is Friday, 29 April.
Programming assignments in CS 485G are individual work. You may discuss approaches with other students, but may not share code or pseudocode for the assignment. If do get ideas from somebody, or use snippets of code from elsewhere, you must cite the source in your documentation.
Added note: There are security problems inherent in an unencrypted protocol such as this one; and with storing passwords in plain text. That is why the ssh protocol was invented. The "Limitations" section of your README should briefly discuss these problems, and other security implications of your program.
csapp.h
. This version
includes the necessary compatibility code to allow the header to be
used from both C
and C++ code. Otherwise you will receive errors about undefined
functions. See the Wikipedia article on
"name
mangling" for more details about why this is necessary.
Also, be careful that you compile csapp.c with a C compiler (gcc), not a C++ compiler (g++). The simplest way to do this is to list the object file csapp.o, rather than the source code csapp.c, as a prerequisite for your executable. That is, if you have a rule like:
rrsh-server: server.cpp csapp.cthen change it to:
rrsh-server: server.cpp csapp.oMake will be smart enough to figure out that it needs to create
csapp.o
using the C compiler.
Clarification: If the command was disallowed, there is no need to fork.
Networks have long provided users with the ability to access and control remote computers through remote shells. Examples include rsh, telnet, and ssh. Each has different features and security characteristics. The remote machine (i.e., the machine being controlled) runs a server process that accepts connections, reads the user's identity over the TCP connection, verifies that user is allowed to access the system, sends a response over the TCP connection to say it is ready for commands. It then repeatedly reads input from the user and returns output from the commands until the session ends (e.g., the user types exit). The user's machine runs a client application that initiates a connection to the remote server, sends the user's name and password and then waits for the server to send back a message granting access. It then reads input from the user, sends it to the remote server to be executed, and then waits for the server to send back the command's output, at which point it starts the process all over again.
In this project you will implement a very simple, and far more restrictive, remote shell. You will write a Restricted Remote Shell server (called rrsh-server). It restricts access in two ways. First, your rrsh-server will wait for users to connect and then will consult a configuration file (rrshusers.txt) to verify that the user is allowed to access the rrsh-server and has provided the correct password. Second, the rrsh-server will verify that the commands typed by the user are allowed commands. In particular, the rrsh-server will look in a configuration file called rrshcommands.txt for a list of allowable programs (commands) that can be run. Commands that are not in the list will be denied by your rrsh server; this is important not just for correctness, but for the security of your account.
You will also write the rrsh-client program. The rrsh-client program will read commands from the user's terminal and then pass them on to the rrsh-server to be executed on the remote machine.
The goal of this project is to help you understand the basics about network programming.
Your rrsh-server should listen for incoming TCP connections using the IPaddress INADDR_ANY and a TCPport number specified as a command line parameter to your rrsh-server. Your rrsh-server will be invoked with the command
./rrsh-server TCPportwhere TCPport is the port number on which your rrsh-server will listen for incoming connections. See the example echo server from the notes and textbook as an example of how to create a server listening on a particular TCP port.
Your rrsh-server will read two config files from the current working directory at startup:
The ``login protocol'' used by the rrsh-client to access the rrsh-cserver will exchange the following information:
'\n'
.
'\n'
.
"Login Approved\n"
or "Login Failed\n"
.
If the login failed, the server will close the connection.
After a user has successfully "logged in" to the server, the client will
begin sending commands to the server. Each command line can be up to 128
characters long and will end with '\n
'.
The server will parse the command to determine what program to
run: the program will be the first token on the command line.
You may wish to use the parser from
program 4, but rrsh should ignore
any redirections specified in the command—this is important
for the safety of your account! As long as your server is
running, anyone in the world can run any of the commands it allows.
The server then checks to see
that the requested program is listed in the
rrshcommands.txt files. If the command is not allowed (i.e., cannot
be found in the rrshcommands.txt file), the rrsh-server will return
(i.e., send) the string
"Cannot execute 'program' on this server\n"
,
where program is the name of the executable
program (file) that was not found in the
rrshcommands.txt file.
If it is allowed, the server will then fork()
and
execv()
(not execvp
)
a child to handle the user's command.
Before calling execv()
, the child process must perform the
necessary dup2()
operations so that:
/dev/null
.dup2()
twice with the same oldfd and different
newfds.
When the child process terminates (whether successful or not), or after
sending the "Cannot execute..."
message, the
rrsh-server (parent process) will send the string
"\nRRSH COMMAND COMPLETED\n"
to the rrsh-client, so that the client will know that the command finished
and it can now send another command. The initial newline is necessary in
case the command's output did not end in a newline, so that the client
is guaranteed to see the string on its own line.
Your rrsh-server (i.e., the parent process) should print out the following information to the terminal (standard output):
"User username logging from IPaddr at TCP port PortNumber.\n"
"User username successfully logged in.\n"
"User username denied access.\n"
"User username sent the command 'CommandLine' to be executed.\n"
"Executing the command 'CommandLine' on behalf of username.\n"
"The command 'CommandLine' is not allowed."
"Cannot execute 'program' on this server\n"
You will also write the code for the rrsh-client program. You will invoke the rrsh-client program with the following command line arguments:
./rrsh-client MachineName TCPportwhere the MachineName specifies the DNS name or the IP address (in dotted decimal format) where the rrsh-server is running, and TCPport is the port number where the rrsh-server is listening.
Your rrsh-client program with then prompt the user for a username, and then for a password (printing each prompt on a new line). Having read the user's response to these questions, the rrsh-client will establish a TCP connection to the rrsh-server and send the login information (as describe above). If the login fails, the client will terminate.
If the login succeeds, the
rrsh-client will print the prompt "rrsh> "
and will read a command
from the user. It will then send the command (unmodified) to the rrsh-server and wait for
the output from the command. When the command completes, your rrsh-client will
receive the string
"RRSH COMMAND COMPLETED"
on its own line, which indicates the
command finished.
The rrsh-client should print to the terminal all of the program's output,
up to but not including the string
"RRSH COMMAND COMPLETED"
.
This particular string is only to be
read by the client, and should not be displayed to the user.
At this point your rrsh-client will
reissue the prompt, read another command, send it to the rrsh-server, etc.
It will continue to process commands until the command entered by the user
is "quit"
. When the rrsh-client reads a quit
command, it will terminate (without sending the command to the server).
Socket
,
Bind
, etc.); or
Note: If you are using C++ for your project, use this version of csapp.h instead, to avoid linker errors about undefined functions.
Your program should compile with the -Wall
compiler flag
(enable all warnings), without producing any compiler or linker warnings
or errors.
It must be possible to compile and run your program on the
class virtual machines. The sequence
of commands make clean; make
should build both parts of
your program without any manual intervention.
If any system call or library function fails, your program should report
the reason for a failure
in an error message to stderr
, and either (1) handle and
correct the error,
(2) terminate the program (or the child process), or (3) (in built-in
commands only) end the command without terminating the program.
If your program does terminate
because of an error, it should indicate this by calling exit
with a nonzero status code.
For this assignment, you may use the wrapper functions provided in
csapp.c.
These functions, with names beginning with a capital letter (Open
,
Socket
, and so on) call the underlying (lowercase) system call or library
function, then print an appropriate error message and exit the program if there
was an error.
We recommend that you start by modifying the echo client and server provided
in the Lecture 16 code, and in the
textbook
under directory netp/
.
You will need to modify the client and server to talk the rrsh protocol described above. In particular, the client must read several lines from the server, until it sees the line "RRSH COMMAND COMPLETED"; and the server must execute the lines it reads as commands, rather than simply echoing them back to the client. You will also need to add code to read the configuration files, and then check the login username against the rrshusers.txt configuration file on each login, and the command against the rrshcommands.txt configuration file on each command.
Your code must compile and run on your VM. If you want to write your code on another machine and port it to your VM later, that is up to you, but it is your responsibility to get the code running on your VM before you submit it.
You can run both your server and your client on your VM. If you would like to run your rrsh-client on other machines to be sure it works from anywhere in the Internet, any Linux machine should work. OS X might work as well, but there are no guarantees there.
An example rrsh-client session might look like the following:
bash $ ./rrsh-client 127.0.0.1 4850 Username: testuser Password: LetMeIn Login Approved rrsh > /bin/ls /tmp config-err-gUsMxg config-err-I4qlYd qt_temp.jd2802 qt_temp.jd4494 rrsh > /bin/cat /etc/issue Ubuntu 14.04.3 LTS \n \l rrsh > /bin/grep 14 /etc/issue Cannot execute '/bin/grep' on this server rrsh > quit bash $ ./rrsh-client 127.0.0.1 4850 Username: testuser Password: WrongPassword Login Failed bash $
A corresponding server session might look like the following:
bash $ ./rrsh-server 4850 User testuser logging in from 192.168.128.9 at TCP port 9648. User testuser successfully logged in. User testuser sent the command '/bin/ls /tmp' to be executed. Executing the command '/bin/ls /tmp' on behalf of testuser. User testuser sent the command '/bin/cat /etc/issue' to be executed. Executing the command '/bin/cat /etc/issue' on behalf of testuser. User testuser sent the command '/bin/grep 14 /etc/issue' to be executed. The command '/bin/grep 14 /etc/issue' is not allowed. User testuser disconnected. User testuser logging in from 192.168.128.9 at TCP port 9648. User testuser denied access. ^C bash $
The example configuration files that would go with the above session could be:
rrshusers.txt
testuser LetMeIn cs485user p4password
rrshcommands.txt
/bin/ls /bin/cat /bin/wc
Your submission should include a README
file. This should
be a plain text file with at least the following sections:
main
and helper functions",
or "README: this file".
csapp.c
functions?
How does the client copy command output to the user, and how does it tell
when a command's output has finished? How do you handle both numeric IP
addresses and string hostnames?
Did you implement any additional
helper functions, for error-handling or other purposes?
If you have any unimplemented features, memory errors, etc., also describe those problems here. Where do the problems occur, what steps did you take to try to solve them, and what further steps would you take if you had more time?
Likewise, if you noticed in your own testing any situations that your shell does not handle well, describe them here.
If you are aware of any security flaws in the design or implementation of the protocol, identify them here. What are potential problems with the protocol being unencrypted? With unencrypted passwords appearing the the configuration files?
The design of the
frobulate()
function benefitted from discussions with my tutor J. Random Hacker, who suggested doing the bit-twiddling before the loop rather than inside the loop.
Likewise, if you used code snippets from sites such as Stack Overflow, describe what you used, explain how it works in your own words, and provide the name of the author and the URL where they posted the code. For example:
Programming assignments are expected to be your own work, so any borrowed code should be very small relative to the total size of your program. You may not borrow code from or share code with other UK students at all.The data-copying loop in
perform_magic()
is based on code written by C. Guru at:http://answermyprogrammingquestions.com/0xc0debeef/
. The loop casts the character pointer to a integer pointer and uses that to copy four bytes of data at a time. If the number of bytes was not divisible by four, the code then copies the remaining 1-3 bytes individually.
Submit a zip or tar.gz file containing a directory with the following files:
rrsh-server
and rrsh-client
. You may start with the Makefile from
one of the previous programming assignments or lectures, or you may
create a new one.
To make a .tar.gz archive of the directory program5, you can use a command like:
tar czvf cs485-program5.tar.gz program5/To make a .zip archive:
zip -r cs485-program5.zip program5/
Submit your .tar.gz or .zip file at the CS Portal website, under course CS485G006 and assignment "Programming Assignment 5 - rrsh".
This assignment will be scored out of 100 points:
-Wall
.csapp.c
wrapper functions,
or you may implement your own error handling as program 4.