[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
CVS gsasl/doc
From: |
gsasl-commit |
Subject: |
CVS gsasl/doc |
Date: |
Mon, 04 Oct 2004 02:44:38 +0200 |
Update of /home/cvs/gsasl/doc
In directory dopio:/tmp/cvs-serv7132
Modified Files:
gsasl.texi
Log Message:
(Using the Library): Rewrite.
--- /home/cvs/gsasl/doc/gsasl.texi 2004/10/03 22:20:00 1.78
+++ /home/cvs/gsasl/doc/gsasl.texi 2004/10/04 00:44:36 1.79
@@ -852,183 +852,326 @@
@node Using the Library
@chapter Using the Library
address@hidden,18cm,9cm}
-
-After initialization of the library, the core part of the library is
-run within a loop until it has finished. The library is handed input
-from the other protocol entity and results in output which is to be
-sent to the other entity, or an error code. The library does not send
-data to the server itself, but only return it in buffers. The main
-interface to the library uses binary data, but since many common
-protocols uses Base 64 encoded data, a wrapper around the main
-function is also provided.
-
-The following pseudo code illustrates how the library is used in a
-simple client. All the functions used are explained later on in this
-manual.
+Your application's use of the library can be roughly modeled into the
+following steps: library initialization, optionally specifying a
+callback and/or properties, perform actual authentications, and
+finally cleaning up. The following image illustrate this.
+
address@hidden,15cm,5cm}
+
+The third step may look the most complex, but for a simple client it
+will actually not involve any code. If your application need to
+handle several concurrent clients, or if it is a server that need to
+serve many clients simultaneous, things do get a bit more complicated.
+
+For illustration, we will write a simple client. Writing a server
+would be similar, the only difference is that instead of supplying
+username or passwords, you need to decide whether someone should be
+allowed to log in or not. The code for what we have discussed so far
+make up our @code{main} function in our client (@pxref{Example 1}):
@example
-main()
+int main (int argc, char *argv[])
@{
- Gsasl_ctx *ctx;
- Gsasl_session_ctx *cctx;
- char *input, output[BUFFERSIZE];
- size_t output_len;
+ Gsasl *ctx = NULL;
int rc;
- rc = gsasl_init (&ctx);
- if (rc != GSASL_OK)
- die(gsasl_strerror(rc));
+ if ((rc = gsasl_init (&ctx)) != GSASL_OK)
+ @{
+ printf ("Cannot initialize libgsasl (%d): %s",
+ rc, gsasl_strerror (rc));
+ return 1;
+ @}
- /* XXX Set callbacks here */
+ gsasl_property_set_global (ctx, GSASL_AUTHID, "jas");
+ gsasl_property_set_global (ctx, GSASL_PASSWORD, "secret");
- /* Read supported SASL mechanism from server */
- input = read_from_client();
+ client (ctx);
- /* Select a good mechanism */
- mech = gsasl_client_suggest_mechanism (ctx, input);
- if (mech == NULL)
- die("Cannot find any commonly agreed SASL mechanism...");
+ gsasl_done (ctx);
- /* Start to use it */
- res = gsasl_client_start (ctx, mech, &cctx);
- if (res != GSASL_OK)
- die(gsasl_strerror (rc));
+ return 0;
address@hidden
address@hidden example
- input = NULL;
- do
- @{
- /* Do one SASL step and unless we're done, send the output to
- server and read new data from server */
+Here, the call to the function @code{client} correspond to the third
+step in the image above.
- rc = gsasl_client_step_base64 (cctx, input, output, BUFFERSIZE);
- if (rc != GSASL_NEEDS_MORE && rc != GSASL_OK)
- break;
+For a more complicated application, that have several clients running
+simultaneous, instead of simply calling @code{client}, it may have
+created new threads for each session, and call @code{client} within
+each thread. The library is thread safe.
+
+An actual authentication session is more complicated than what we have
+seen so far. The steps that make up it are: decide which mechanism to
+use, start the session, optionally set any additional properties,
+perform the actual authentication loop, and cleanup. Naturally, your
+application will start to talk its own protocol (e.g., SMTP or IMAP)
+after these steps have concluded.
+
+The authentication loop is based on sending tokens (typically short
+messages encoded in base 64) back and forth between the client and
+server. It continue until authentication succeeds or there is an
+error. The actual data sent, the number of iterations in the loop,
+and other details are specified by each mechanism. The goal of the
+library is to isolate your application from the details of all
+different mechanisms. Note that the library do not send data to the
+server itself, but return it in an buffer. You must send it to the
+server yourself, according to an application protocol profile. For
+example, the @acronym{SASL} application protocol profile for
address@hidden is described in @acronym{RFC} 2554.
- write_to_server(output);
+The following image illustrate the steps we have been talking about.
- if (rc == GSASL_OK)
- break;
address@hidden,14cm,10cm}
- input = read_from_server();
- @}
- while (rc == GSASL_NEEDS_MORE);
+We will now show the implementation of the @code{client} function used
+before.
- if (rc != GSASL_OK)
- die("Authentication failed... %s\n", gsasl_strerror(rc);
address@hidden
+void client (Gsasl *ctx)
address@hidden
+ Gsasl_session *session;
+ const char *mech = "PLAIN";
+ int rc;
- /* Client is now authenticated -- proceed with actual protocol... */
+ /* Create new authentication session. */
+ if ((rc = gsasl_client_start (ctx, mech, &session)) != GSASL_OK)
+ @{
+ printf ("Cannot initialize client (%d): %s\n",
+ rc, gsasl_strerror (rc));
+ return;
+ @}
- gsasl_client_finish (cctx);
- gsasl_done (ctx);
+ /* Do it. */
+ client_authenticate (ctx, session);
+
+ /* Cleanup. */
+ gsasl_finish (session);
@}
@end example
-Notice the XXX comment that said you should specify the callbacks to
-use there. The GNU SASL Library depend on callbacks to implement user
-interaction (in the client) and user validation (in the server). If
-you don't specify any callbacks, very few mechanisms will be supported
-(like EXTERNAL that don't need any additional information,
address@hidden). Since we are building a simple client, we define
-callbacks which are used by several SASL mechanisms to get username
-and password. We start by defining the function for querying the
-username, following the prototype for
address@hidden for the LOGIN mechanism
-(@pxref{LOGIN}) .
-
address@hidden
-int
-callback_username (Gsasl_session_ctx *ctx,
- char *out,
- size_t *outlen)
+This function is responsible for deciding which mechanism to use. In
+this case, the @samp{PLAIN} mechanism is hard coded, but you will see
+later how this can be made more flexible. The function create a new
+session, then call another function @code{client_authenticate}, and
+end by cleaning up the session handle. Let's continue with the
+implementation of @code{client_authenticate}.
+
address@hidden
+void client_authenticate (Gsasl * ctx, Gsasl_session * session)
@{
- char username[BUFFERSIZE];
+ char buf[BUFSIZ] = "";
+ char *p;
+ int rc;
+
+ /* This loop mimic a protocol where the server get to send data
+ first. */
- if (out == NULL)
- *outlen = BUFFERSIZE;
- else
+ do
+ @{
+ printf ("Input base64 encoded data from server:\n");
+ fgets (buf, sizeof (buf) - 1, stdin);
+
+ rc = gsasl_step64 (session, buf, &p);
+
+ if (rc == GSASL_NEEDS_MORE || rc == GSASL_OK)
+ @{
+ printf ("Output:\n%s\n", p);
+ free (p);
+ @}
+ @}
+ while (rc == GSASL_NEEDS_MORE);
+
+ printf ("\n");
+
+ if (rc != GSASL_OK)
@{
- fprintf(stdout, "Enter username: ");
- fgets(username, BUFFERSIZE, stdin);
- *outlen = strlen(username);
+ printf ("Authentication error (%d): %s\n",
+ rc, gsasl_strerror (rc));
+ return;
@}
- return GSASL_OK;
+ /* The client is done. Here you would typically check if the
+ server let the client in. If not, you could try again. */
+
+ printf ("If server accepted us, we're done.\n");
@}
@end example
-As you can see, this is a simplistic function that reads a username
-from the user. The callback for entering the password is similar and
-follows the @code{Gsasl_client_callback_password} prototype:
+This last function need to be discussed in some detail. First, you
+should be aware that there are two versions of this function, that
+differ in a subtle way. The version above (@pxref{Example 2}) is used
+for application profiles where the server send data first. For some
+mechanisms, this may waste a roundtrip, because the server need input
+from the client to proceed. Therefor, today the recommended approach
+is to permit client to send data first (@pxref{Example 1}). Which
+version you should use depend on which application protocol you are
+implementing.
+
+Further, you should realize that it is bad programming style to use a
+fixed size buffer. On GNU systems, you may use the @code{getline}
+functions instead. However, in practice, there are few mechanisms
+that use very large tokens. In typical configurations, the mechanism
+with the largest tokens (GSSAPI) can use at least 500 bytes. A fixed
+buffer size of 8192 bytes may thus be sufficient for now. But don't
+say I didn't warn you, when a future mechanism doesn't work in your
+application, because of a fixed size buffer.
+
+The @code{gsasl_step64} (and of course also @code{gasl_step}) return
+two non-error return codes. @code{GSASL_OK} is used for success,
+indicating that the library consider the authentication finished.
+That may include a successful server authentication, depending on the
+mechanism. You must not let the client continue to the application
+protocol part unless you receive @code{GSASL_OK} from these functions.
+In particular, don't be fooled into believing authentication were
+successful if the server reply ``OK'' but these function has failed
+with an error. The server may have been hacked, and could be tricking
+you into sending confidential data, without having successfully
+authenticated the server.
+
+The non-error return code @code{GSASL_NEEDS_MORE} is used to signal to
+your application that you should send the output token to the peer,
+and wait for a new token, and do another iteration. If the server
+conclude the authentication process, with no data, you should call
address@hidden (or @code{gsasl_step}) specifying a zero-length
+token.
+
address@hidden Choosing a mechanism
+
+Our earlier code was hard coded to use a specific mechanism. This is
+rarely a good idea. Instead, it is recommended to select the best
+mechanism available from the list of mechanism supported by the
+server. Note that without TLS or similar, the list may have been
+maliciously altered, by an attacker. This means that you should abort
+if you cannot find any mechanism that exceeds your minimum security
+level. There is a function @code{gsasl_client_suggest_mechanism}
+(@pxref{Global Functions}) that will try to pick the ``best''
+available mechanism from a list of mechanisms. Our simple interactive
+example client (@pxref{Example 3}) include the following function to
+decide which mechanism to use. Note that it doesn't blindly use what
+is returned from @code{gsasl_client_suggest_mechanism}.
@example
-int
-callback_password (Gsasl_session_ctx *ctx,
- char *out,
- size_t *outlen)
+const char *client_mechanism (Gsasl *ctx)
@{
- char password[BUFFERSIZE];
+ static char mech[GSASL_MAX_MECHANISM_SIZE + 1] = "";
+ char mechlist[BUFSIZ] = "";
+ const char *suggestion;
+
+ printf ("Enter list of mechanism that server support, separate by SPC:\n");
+ fgets (mechlist, sizeof (mechlist) - 1, stdin);
+
+ suggestion = gsasl_client_suggest_mechanism (ctx, mechlist);
+ if (suggestion)
+ printf ("Library suggest use of `%s'.\n", suggestion);
+
+ printf ("Enter mechanism to use:\n");
+ fgets (mech, sizeof (mech) - 1, stdin);
+ mech[strlen (mech) - 1] = '\0';
- if (out == NULL)
- *outlen = BUFFERSIZE;
- else
- @{
- fprintf(stdout, "Enter password: ");
- fgets(password, BUFFERSIZE, stdin);
- *outlen = strlen(password);
- @}
-
- return GSASL_OK;
+ return mech;
@}
@end example
-In reality, the program should probably inhibit echo of the password
-to the terminal, but that is left as an exercise for the reader.
address@hidden Using a callback
+
+Our earlier code specified the username and password before the
+authentication loop, as in:
+
address@hidden
+ gsasl_property_set_global (ctx, GSASL_AUTHID, "jas");
+ gsasl_property_set_global (ctx, GSASL_PASSWORD, "secret");
address@hidden example
+
+This may work for simple mechanisms, that only ever need an username
+and a password. But some mechanism require more information, like
+authorization identities, a special PIN or passcode. Querying the
+user for all that information, without knowing exactly which of it
+will be really needed is bad design.
+
+This is a bad idea for another reason. What if the server abort the
+authentication process? Then your application have already queried
+the user for a username and password. It would be better if you only
+asked the user for this information, annoying to input, when it is
+known to be needed.
+
+A better approach to this problem is to use a callback. Then the
+mechanism may query your application whenever it need some
+information, like the username and password. It will only do this at
+the precise step in the authentication when the information is
+actually needed. Further, if the user abort a password prompt, the
+mechanism is informed of this, and could recover somehow.
-Now having implemented the callbacks, we are ready to replace the XXX
-comment with real code that set the callbacks (@pxref{Callback
-Functions}). The following does it.
+Our final example (@pxref{Example 4}) specify a callback function,
+inside @code{main} as below.
@example
- gsasl_client_callback_authentication_id_set(ctx, callback_username);
- gsasl_client_callback_authorization_id_set(ctx, callback_username);
- gsasl_client_callback_password_set(ctx, callback_password);
+ /* Set the callback handler for the library. */
+ gsasl_callback_set (ctx, callback);
address@hidden example
+
+The function itself is implemented as follows.
+
address@hidden
+int callback (Gsasl * ctx, Gsasl_session * sctx, Gsasl_property prop)
+{
+ char buf[BUFSIZ] = "";
+ int rc = GSASL_NO_CALLBACK;
+
+ /* Get user info from user. */
+
+ printf ("Callback invoked, for property %d.\n", prop);
+
+ switch (prop)
+ {
+ case GSASL_PASSCODE:
+ printf ("Enter passcode:\n");
+ fgets (buf, sizeof (buf) - 1, stdin);
+ buf[strlen (buf) - 1] = '\0';
+
+ gsasl_property_set (sctx, GSASL_PASSCODE, buf);
+ rc = GSASL_OK;
+ break;
+
+ case GSASL_AUTHID:
+ printf ("Enter username:\n");
+ fgets (buf, sizeof (buf) - 1, stdin);
+ buf[strlen (buf) - 1] = '\0';
+
+ gsasl_property_set (sctx, GSASL_AUTHID, buf);
+ rc = GSASL_OK;
+ break;
+
[61 lines skipped]
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc,
gsasl-commit <=
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc, gsasl-commit, 2004/10/03
- CVS gsasl/doc, gsasl-commit, 2004/10/04
- CVS gsasl/doc, gsasl-commit, 2004/10/04
- CVS gsasl/doc, gsasl-commit, 2004/10/04
- CVS gsasl/doc, gsasl-commit, 2004/10/12
- CVS gsasl/doc, gsasl-commit, 2004/10/12
- CVS gsasl/doc, gsasl-commit, 2004/10/15