The Cosign Web Single Sign-On Scheme
June 2006
Draft 4
Craig, Craig, McGowan, and Malestein
cosign@umich.edu
Introduction
We present the overall design scheme of Cosign, an open source web single sign-on package. We describe the functionality of the three major parts of Cosign - the filter, the CGIs, and the daemons. We also describe the supporting components like cookie values, the query string protocol, template structure, local cookie cache file formats, and the SMTP-like, SSL encrypted Cosign Protocol that the filters and the CGIs use to communicate with the daemon.

Authentication Sequence ( Overview )

The process begins when the user accesses an area on the web server for which the filter is configured to require authentication. The filter checks for the presence of a valid service cookie, and if not present, sets a new one and redirects the user's browser to the Cosign CGI, including the service cookie and a return URL in the query string.

The Cosign CGI, in turn, checks for the presence of a valid login cookie. If present, the CGI registers the new service cookie with the existing login cookie, and redirects the user's browser back to the return URL; this is single sign-on (authentication without additional password prompting) in action. If the login cookie is not present, however, the Cosign CGI presents an authentication form, and upon successful authentication, creates the login cookie, registers the service cookie with it, and redirects the user's browser to the return URL.

When the user's browser returns to the application, the filter again checks for a valid service cookie, and finds it. The filter then populates the environment with the user’s authentication information and permits access to the application.

Cookies
There are two different classes of cookie used by Cosign - a login cookie and a service cookie. Both cookies are host cookies, so only the host that sets them has access to retrieve them. New cookie values are 128 random characters. The character “/” is not allowed because Cosign uses it as a separator. These strings are generated using OpenSSL.

Login Cookie
Login cookies are the keys by which a given browser identifies itself to the cosign server, and are generated by the CGI. They are of the format cosign=[128 character string of random bytes]/time_the_cookie_was_generated/registration_count. 

An example is:

	cosign=4qkW7lHyvaVThEzuOxss28cQwURP846TFSs+TnanvI2P/1132593978/1

The time stamp is used to make sure old login cookies retained by certain browsers can still be refreshed. If a login cookie is older than the default of 24 hours a new cookie is issued. The registration count is used in the detection of register loops. If a user visits the login pages more than MAXLOOPCOUNT (10) times in LOOPWINDOW (30) seconds, their browser is redirected to a loop breaking page to advise them of the registration problem.

Service Cookie
Service cookies are the keys by which a given browser identifies itself to the application server, and are generated by the filter. They are of the format cosign-service_name=[128 character string of random bytes]/time_the_cookie_was_generated. Upon creating a new cookie, the filter prepends "cosign-" to the service name as defined by the filter's cosign service configuration. An example service cookie, (where the cosign service has been set to "example") looks like this:

	cosign-example=oFxNMqPjNuSraSY+lIqDAzU1UDfsbp2JaHvm6mRP5jhRoIR...

The time stamp is used to make sure old service cookies retained by certain browsers can still be refreshed. If a service cookie is older than the cookie expire time as set by the filter's configuration, a new cookie is issued.

Filter
The filter resides on an application server, and is not part of the centralized cosign infrastructure. The filter is responsible for determining which areas of a web site are protected by cosign and which are not. If a user attempts to access a protected area, the filter assures the user is authenticated, and obtains their username, authentication realm, IP address, and optionally a Kerberos ticket and passes that information along to the protected applications. This information can then be used by other authorization mechanisms to make further access decisions. 

The most commonly used implementation of the filter is an Apache module that is called in the “check access” and “user authentication” phases of the request/response loop; filters for the Microsoft IIS and Java servlet environments are also available, and are integrated similarly.

For an extremely in depth description of the filter architecture see:

	http://weblogin.org/filter.html

Query String - (protocol "front side")
The query string is used to pass information between the filter and the CGI, and by the CGI to pass information to itself. The following describes the query string of the user's browser as it is redirected, and not a separate server-to-server query.

Filter
The information sent to the CGI on the query string consists of the registration URL as specified by the filter's configuration, a question mark denoting the start of the query string data, the list of required factors if any are configured, the user's service cookie, and the URL to redirect the browser back to once a REGISTER (see Protocol below) is successfully completed. This information takes the following format:

	register-url?[[factors=factor1[,factor2]...&]service=cookie[;]&referring-url 

For example:

	http://register.example.edu/cosign-bin/cosign.cgi?factors=ldap&cosign-example=sdfl54kjylk45jy..ert&https://cosign.example.edu/start.html

The service cookie is followed by an optional yet deprecated semi-colon.

CGI
The cgi can pass to itself an additional argument on the query string beyond what it receives from the filter. The keyword basic can optionally be passed from the cgi to itself to check that cookies are enabled in the browser. Additionally basic means that the cgi is operating in a BasicAuth compatible mode and that the REMOTE_USER environment variable is set.

	register-url?[basic&][factors=factor1[,factor2]...&]service=cookie[;]&referring-url 

For example:

	http://weblogin.example.edu/cosign-bin/cosign.cgi?basic&factors=EXAMPLE.EDU&cosign-example=sdfl54kjylk45jy..ert&https://cosign.example.edu/start.html

CGIs
Cosign has 2 cgis - cosign.cgi and logout. 

Cosign.cgi
The "login" cgi (cosign.cgi) is responsible for logging users into and out of the central cosign server. It is also responsible for registering each service a user logs into - this action ties the user's central login cookie to their session on individual application servers such as our web mail client, web directory client, or CourseTools environment. The prototype CGI was built to use Kerberos V/GSSAPI to authenticate the user. Guest accounts via Cosign Friend and a MySQL database are also supported, as is authentication with Apache's BasicAuth and x509 certificates. Cosign 2.x supports external authenticators with a new API. This change allows the cgi to use an arbitrary login method by calling out to an external program.

The CGI also has the ability to prompt the user to re-enter their password in order to access any service that has registered for re-authentication. This means that before the CGI will REGISTER a service cookie for a service configured for re-authentication, the user must successfully authenticate again. This gives a slightly better assurance that the user is who she says she is, and is considered more secure.

Logout
The logout cgi is responsible for logging users out of the central cosign server. Once a logout has been verified, the logout CGI clobbers the login cookie by writing a cookie with the value "null" and setting the cookie's expiration date to a time in the past. Since all state is maintained centrally, the user is immediately logged out of every application she has visited during her session, with one notable exception.

Due to the implementation of data caching in the filters, the most recent application a user has visited will still report the user as logged in for the cache time (default is 60 seconds). In order to avoid this lag, a local logout script can be run on the application server to expire the local cosign-service cookie. This marks the user as logged out locally, and can then redirect the user to the central logout script. After the logout has been verified, the user is truly logged out of all cosign protected services.

Templates
The templates used by the Cosign CGIs fall into two broad categories – dynamic and static.

Dynamic Pages
The tables below list the required fields for the “dynamically generated” cosign pages. They are dynamic in that the $l in the template for reauth.html will be replaced by the CGI with the login name of the currently logged in user. Fields like “verify” in verify-logout.html are required, but their value can be determined on a per installation basis. 

Page Name	login.html	
draws the login screen for initial logins using cosign.cgi	
Action	post	/cosign-bin/cosign.cgi	
			
Value	Form Field Name	Description	
$t		the page title, e.g. CoSign: $t	
$r	referrer	URL to redirect upon successful login	
$c	service	the service cookie received on the 
query string	
$f		required factors from filter via query string	
$d		required factors already satisfied via CHECK	
$l	login	the user id of the person attempting to
login	
	password	field for user to enter their password	
$e		optional "error" field to add helpful text	
	
Page Name	error.html	
reports any non-retryable errors from cosign.cgi	
Action	n/a	n/a	
			
Value	Form Field Name	Description	
$t		the page title, e.g. CoSign: $t	
$e		field to print relevant error message	

Page Name	login_error.html	
draws login screen when a retryable error is encountered by cosign.cgi	
Action	post	/cosign-bin/cosign.cgi	
			
Value	Form Field Name	Description	
$t		the page title, e.g. CoSign: $t	
$r	referrer	URL to redirect upon successful login	
$c	service	the service cookie received on the 
query string	
$f		required factors from filter via query string	
$d		required factors already satisfied via CHECK	
$l	login	the user id of the person attempting to
login	
	password		
$e		field to print relevant error message	

Page Name	verify-logout.html	
draws login screen when a retryable error is encountered by cosign.cgi	
Action	post	/cosign-bin/logout	
			
Value	Form Field Name	Description	
$t		the page title, e.g. CoSign: $t	
$u	url	URL to redirect upon successful login	
Logout	verify	submit button	

Page Name	reauth.html	
draws login screen for re-authentication	
Action	post	/cosign-bin/cosign.cgi	
Action	post	/cosign-bin/logout	
			
Value	Form Field Name	Description	
$t		the page title, e.g. CoSign: $t	
$r	referrer	URL to redirect upon successful login	
$u	url	URL to redirect upon successful logout	
$c	service	the service cookie received on the 
query string	
$f		required factors from cgi’s reauth config	
$l	login	the user id of the person attempting to
login	
	password	field for user to enter their password	
true	reauth	flag for CGI	

Static Pages
The following pages are required by cosign.cgi, but have no dynamically generated content.

post_error.html – this is the page the filter will redirect users to should a REGISTER be required during a POST. Cosign does not currently support REGISTERing through a POST, and as such, a user’s data would be lost. Instead the user sees a splash page that tells them their changes were not saved, some ways to avoid the problem in the future, and gives a link to the login page.

looping.html – this is the page cosign.cgi redirects a user to when the CGI has detected a browser loop. These are caused by ill configured filters or badly behaving browsers. This page has some helpful text to let the user know what might be the cause of the loop and how to contact the support staff.

/services/ – by default, a successful login with no referrer destination URL will be redirected to https://weblogin.example.com/services/. We call this the “service menu” and it is an ideal jumping off point for listing your institution’s cosign protected services.

Daemons
Cosign has two daemons - cosignd and monster. Cosignd is responsible for implementing the server side of the cosign protocol as well as maintaining the cosign cookie database and optionally replicating the LOGIN, REGISTER, and LOGOUT commands to all other cosignds in the server pool. Monster is responsible for deleting old cookies (login or service) as well as corresponding Kerberos tickets from the database. Monster also optionally replicates the logged in/out state and last update time for all of the login cookies in its database via the TIME command.

Cosignd
Cosignd, also known as the central daemon, is the server side implementation of the cosign protocol (see Protocol below). The central daemon is responsible for maintaining the state of all cosign sessions. This includes keeping track of which users have logged in, logged out, and idle timed out. This also means the daemon keeps track of all of the service cookies that represent the authenticated web applications a user has accessed. The daemon has the ability to replicate its cookie database to multiple hosts, so a failure of one server does not constitute a failure of the system. The daemon answers queries of user identity from both the cgi and the filter, and talks to other daemons. 

Monster
Monster serves two separate functions. In all cases, it is the mechanism by which old cookies and tickets can be deleted when they are  no longer needed. In addition, in a replicated cosign environment, monster can be used to propagate the time stamps and state of the login cookies. If replication is activated, monster retrieves the host information for the other hosts in the cosign pool and opens an SSL connection to the cosignds on all of the other cosign hosts.
Monster sleeps for timestamp-pushing-interval (120 seconds) between successive passes  through the cookie database. Monster stats each login cookie, checking to see if it is a) past the idle-time-out (4.5 hours, which is idle time + grey window from cosignd, + the  usual loggedout_cache time in monster), b) the hard-timeout (12 hours), or c)  if the cookie state is already logged out and the cookie is past the loggedout-keep-time (2 hours). If it is past these timeouts, the login cookie is deleted, as well as any corresponding Kerberos tickets. When monster encounters a service cookie, it looks up the corresponding login cookie and checks for timeouts as specified above. If any are encountered, it deletes the login cookie as well as the service cookie. If the service cookie’s corresponding login cookie has already been deleted, the service cookie is also deleted.
Local Cookie Stores
The daemon and the filters have the following format for the local cookie store:

Filter
	
i	ip address
p	principal (user name)
r	realm (first factor)
f	all factors
k	kerberos ticket path (optional, if built in)

For example:

i66.93.92.59
ptestuser
rUMICH.EDU
fUMICH.EDU mtoken
k/ticket/EjAP2EqDy-5

Daemon

v	version
s	state		
i	initial ip address
j	subsequent ip address ( same as initial if no change)
p	principal (user name)
r	factor(s)
t	time data was stored in seconds since 1970
k	kerberos ticket path (optional, if built in)

For example:

v2
s1
i141.213.147.32
j141.213.147.32
ptestuser
rUMICH.EDU
t1150894952
k/ticket/EjAP2EqDy-5l

Protocol
Cosignd currently supports the following protocol requests:
	
	QUIT	-	terminate session

		C: QUIT\r\n
		S: 221 Service closing transmission channel\r\n
	
	NOOP	 -	do nothing
		
		C: NOOP\r\n
		S: 250 cosign vVERSION\r\n

	HELP -	display helpful message
		
		C: HELP\r\n
		S: 203 Slainte Mhath! http://weblogin.org\r\n

STARTTLS	-	Start  TLS.  This command must be given before a client can send a LOGIN, REGISTER, CHECK, LOGOUT, RETRIEVE, DAEMON, or TIME command.

		S: 220 Collaborative Web Single Sign-On\r\n
		C: STARTTLS\r\n
		S: 220 Ready to start TLS\r\n
		<TLS negotiation, further commands are under TLS layer>

Starting in Cosign 2.x, protocol version 2 is 

		S: 220 2 Collaborative Web Single Sign-On\r\n
		C: STARTTLS 2\r\n
		S: 220 Ready to start TLS\r\n
		<TLS negotiation, further commands are under TLS layer>
		S: 221 TLS successfully started.\r\n

The commands HELP, QUIT, and NOOP are the only ones available until STARTTLS is called. The exception to this behavior is when cosignd is running in TLS optional mode (-X) and the CN "NOTLS" is listed in cosign.conf. In this case, the full suite of commands is available regardless of calling STARTTLS.

	Error Cases:
	
A 501 is issued if the syntax of the STARTTLS command is wrong or if cosignd is unable to establish a TLS session with the client.

A 502 is issued in v2 if an unknown protocol version number (eg. not 2) is given as the second argument of the STARTTLS command.

A 401 is issued if the host's CN does not appear in cosign.conf as either a cgi or a service, and the tls optional flag ( and corresponding NOTLS entry in cosign.conf ) have not been set.
		
LOGIN -	Associates  a login cookie with a user, n-factors, IP address and timestamp. Used by the cgi once the user has authenticated.

	If no kerberos ticket is to be transferred, the protocol for LOGIN is as follows:

		C: LOGIN login_cookie ip principal realm\r\n
		S: 200 LOGIN successful: Cookie Stored.\r\n

	Protocol v2:

C: LOGIN login_cookie ip principal factor\r\n
		S: 200 LOGIN successful: Cookie Stored.\r\n

If a kerberos ticket is to be transferred, a final argument, the literal "kerberos", is appended to the LOGIN command.

		C: LOGIN login_cookie ip principal realm kerberos\r\n
			or for protocol v2
		C: LOGIN login_cookie ip principal factor kerberos\r\n
		S: 300 LOGIN: Send length then file.\r\n
      		C: [size]\r\n
		C: [data]
		C: .\r\n
		S: 201 LOGIN successful: Cookie & Ticket Stored.\r\n

	Error Cases:

Issuing the LOGIN command for the same cookie multiple times will result in a 202 if no new factors are presented or the ip address has not changed. A 402 is returned if the principal varies between the saved version of the cookie and the principal given in the command. All other errors in his case result in a 5xx. 

A 401 is returned if a client issues the LOGIN command and its CN is not listed as a cgi in cosign.conf. All other errors, including malformed commands and system errors, cause cosignd to return a 5xx.

REGISTER -	Associates a service cookie (cosign-[servicename]=) with a login cookie (cosign=). This command is used by the CGI.

		C: REGISTER login_cookie ip service_cookie\r\n
		S: 220 REGISTER successful: Cookie Stored.\r\n

Error Cases:

Issuing the REGISTER command for the same cookie multiple times will result in a 226 if the information matches exactly. A 421 is issued if the user has already logged out, and a 422 is issued if the user's session has been idle logged out. Finally, 420 is returned if the client's CN is not listed as a CGI in cosign.conf.

52x errors are returned for bad syntax, invalid paths to the cookie database, or if the user's login cookie is in the grey window. The grey window (default is 30 minutes) is the time when, although a user may be logged out, cosignd returns an unknown (5xx) instead of logged out (4xx) to account for lag in the replication of logouts.

LOGOUT	-	Invalidates a user's login cookie, causing any future CHECK commands to return the logged out status. This is a centralized logout. This command is used by the CGI.
		
		C: LOGOUT login_cookie ip\r\n
		S: 210 LOGOUT successful: cookie no longer valid\r\n

Error Cases:

Issuing the LOGOUT command for the same cookie multiple times will result in a 411. A 410 is returned if the client's CN is not listed as a CGI in cosign.conf. A variety of 51x errors are returned for bad syntax, invalid cookie paths, and miscellaneous system errors.

CHECK	-	Allows clients to retrieve information about a user based on the cookie presented to cosignd. This command is used by both the CGI and the filter (service).

		C: CHECK service_cookie\r\n
		S: 231 ip principal realm\r\n

		C: CHECK login_cookie\r\n
		S: 232 ip principal realm\r\n

	Protocol v2:

		C: CHECK service_cookie\r\n
		S: 231 ip principal factor [factor2] [factorN]\r\n

		C: CHECK login_cookie\r\n
		S: 232 ip principal factor [factor2] [factorN]\r\n

Error Cases:
	
A 430 is returned if the client's CN is not listed as a CGI or a service in cosign.conf. A 431 signifies an unknown cookie prefix, meaning the cookie doesn't begin with "cosign=" or "cosign-". A 432 response indicates the user is already logged out, while a 433 means the user 's session has been idle logged out.

53x response codes indicate bad syntax, invalid cookie paths, and that a login cookie is in the grey window (see REGISTER above). Of particular note as they appear in the logs frequently are:

		S: 533 CHECK: cookie not in db!\r\n
	and
		S: 534 CHECK: Who me? Dunno.\r\n

The 533 means the service cookie was not found in that particular cosignd's cookie database, while a 534 means the login cookie was not found in the cookie database. Neither of these errors are of particular concern and are merely side effects of cosign's lossy replication.

RETRIEVE	-	Retrieve a Kerberos credential and/or proxy cookies for n-tier authentication.

The RETRIEVE command takes 2 different keywords - "tgt" and "cookies". In either case, 54x responses are returned for bad syntax, login cookie in the grey window, invalid cookies, missing cookies, or any system errors. 44x responses are returned if the user is already logged out, the user's session has been idle logged out, or the client has asked for a retrieve type other than "tgt" or "cookies." Finally, a 442 is returned if the client's CN is not permitted as a service in cosign.conf.

The keyword "tgt" is used to signify that a client needs a user's tgt in order to perform a kerberos authenticated task on behalf of that user. In this case, the protocol looks like this:

		C: RETRIEVE service_cookie "tgt"\r\n
		S: 240 Retrieving file\r\n
		S: [size]\r\n
		S: [data]
		S: .\r\n

	Error Cases:

A 441 is returned if the client's CN does not have the T flag set in cosign.conf. A 547 is issued if the user's kerberos ticket cannot be read and a 548 stat() fails on the user's kerberos ticket.	

The other keyword is "cookies". This version of RETRIEVE creates and pre-registers service cookies for the user as defined by the proxy file referenced in cosign.conf. This  is for use with applications like UPortal and allows one cosign service to proxy for  several others.

		C: RETRIEVE service_cookie "cookies"\r\n
		S: 241-[cookiename] [hostname to use cookie with]
		S: 241- ...
		S: 241 Cookies registered and sent.

	Error Cases:

A 443 is returned if the client's CN listing in cosign.conf doesn't have the "P" flag set. Any other error is a serious system error which will cause the server to drop the connection.

DAEMON	-	Part of replication, prevents the server from replicating to itself. This command is used by cosignd and monster.

		C: DAEMON hostname\r\n
		S: 271 Daemon flag set.\r\n

Error Cases:

If there is a syntax error in the command, or the gethostname() system call fails, cosignd will return a 57x. If the hostname given as the second argument matches the server's own hostname, the server has connected to itself and should therefore close the replication connection. This is accomplished by issuing:

		S:  471 Schizophrenia!\r\n

	Finally, if the CN is not permitted as a CGI in cosign.conf, a 470 is issued.

TIME	-	Allows daemons to propagate timestamp information and state for login cookies. This command is used by monster.

		C: TIME\r\n
		S: 360 TIME: Send timestamps.\r\n
		C: login_cookie1 timestamp1 state1
		C: login_cookie2 timestamp2 state2
		C: ...
		C: login_cookieN timestampN stateN
		C: .
		S: 260 TIME successful: we are now up-to-date\r\n 

Error Cases:
	
If the CN of the host issuing the TIME command is not listed as a CGI in cosign.conf, the following error is returned:

		S: 460 TIME: hostname not allowed to propogate time.\r\n

TIME takes only one argument, the keyword TIME. Any additional arguments will cause this error:
	
		S: 560 TIME: Wrong number of args.\r\n

Thanks to Cory Snavely for help with this document.