Tcl SASL

TOC

| M. Rose
| Dover Beach Consulting, Inc.
| January 20, 2002

Tcl SASL

### Abstract

Tcl SASL provides a Tcl interface to the Cyrus SASLv2 library.

TOC

### Table of Contents

TOC

### 1. SYNOPSIS

        package provide sasl 2.0

Tcl SASL provides a Tcl interface to the Cyrus SASLv2 library.

This document won't tell you what SASL[1] is. What it will tell you, the Tcl programmer, is how you can access the Cyrus SASLv2 library from your Tcl script.

#### 1.1 Installation

First, you'll need to have Tcl 8.3 (or later) already installed.

        ./configure ... --with-tcl=...

TOC

### 2. CONVENTIONS

#### 2.1 Return Codes

If an exception occurs in a Cyrus SASL library function, an error is thrown and the errorCode global is set to a list containing four elements:

- the literal string "SASL";
- a string naming the Cyrus SASL library function that failed;
- an integer identifying the Cyrus SASL result code that was returned; and,
- a string containing the associated diagnostic.

In addition to the usual "normal" return, four routines (sasl::server_start, sasl::server_step, sasl::client_start, and sasl::client_step) may also use the "continue" return. This occurs when the corresponding Cyrus SASL library function returns SASL_CONTINUE.

#### 2.2 Data Types

A serialized array is a list that contains an series of keywords and values.

A serialized array can be traversed using foreach, e.g.,

        foreach {k v} $aList {
            puts stdout "$k has value $v"
        }

Alternatively, an array can be initialized and accessed, e.g.,

        array set data $aList
        if {[info exists data(plugin)]} {
            puts stdout "plugin entry is present"
        }

Several routines accept serialized arrays as arguments. In particular, the Cyrus SASL security properties is expressed as a serialized array with these elements:

- min_ssf
- max_ssf
- max_bufsize
- flags

With the exception of flags, each of these takes an integer value. The flags entry takes a list value. To find out what flags are known, try sasl::info sec_flags.

#### 2.3 Interactions

Two routines (sasl::client_start and sasl::client_step) have an optional -interact switch that takes a script argument.

When evaluated, the script is given one argument, a serialized array. At a minimum, the id element is present in the serialized array. Optionally, three other elements (challenge, prompt, and default) may also be present.

If the script makes a normal return, the return value is supplied to the Cyrus SASL library.

#### 2.4 Callbacks

Four routines (sasl::server_init, sasl::server_new, sasl::client_init, and sasl::client_new) have a -callbacks switch that takes a list argument. Each element of the list is either:

- the name of a callback; or,
- a list containing two elements, the name of a callback and a script.

The former case is used by clients to tell the Cyrus SASL library what information may be determined using an interaction. Otherwise, the supplied script is evaluated to get the indicated information.

In this latter case, the script is given one argument, a serialized array containing callback-specific elements.

#### 2.4.1 Client/Server callbacks

> getopt:
>   Returns a string.
>   Elements:
>   - ?plugin?
>   - option

> log:
>   Elements:
>   - level
>   - message

> getpath:
>   Returns a string.
>   Elements: none.
> verifyfile:
>   Returns an integer (a SASL result code), either 0 (SASL_OK), 1 (SASL_CONTINUE), or -1 (SASL_FAIL).
>   Elements:
>   - file
>   - type

>   To find out what types are known, try sasl::info verify_types.

#### 2.4.2 Client-only callbacks

> user:
>   Returns a string.
>   Elements: id
> authname:
>   Returns a string.
>   Elements: id
> language:
>   Returns a string.
>   Elements: id
> cnonce:
>   Returns a string.
>   Elements: id
> pass:
>   Returns a string.
>   Elements:
>   - token (a client token)
>   - id

> ?no?echoprompt:
>   Returns a string.
>   Elements:
>   - id
>   - challenge
>   - prompt
>   - default

> getrealm:
>   Returns a string.
>   Elements:
>   - id
>   - ?available? (a list of possible choices)

#### 2.4.3 Server-only callbacks

> proxy:
>   Returns an integer (a SASL result code), usually either 0 (SASL_OK), or -14 (SASL_NOAUTHZ).
>   Elements:
>   - token (a server token)
>   - target
>   - user
>   - ?realm?
>   - ?propctx? (a propctx token)

> checkpass:
>   Returns an integer (a SASL result code).
>   Elements:
>   - token (a server token)
>   - user
>   - pass
>   - ?propctx? (a propctx token)

> setpass:
>   Returns an integer (a SASL result code).
>   Elements:
>   - token (a server token)
>   - user
>   - pass
>   - flags
>   - ?propctx? (a propctx token)

>   To find out what flags are known, try sasl::info setpass_flags.

> canonuser:
>   Returns a string.
>   Elements:
>   - token (a server token)
>   - in
>   - ?realm?
>   - outmax
>   - flags

>   To find out what flags are known, try sasl::info canon_flags.

TOC

### 3. PROCEDURES

#### 3.1 Server-only calls

The calling sequence is:

1. Invoke sasl::server_init to load plugins.
2. On each incoming connection, invoke sasl::server_new to get a server token.
3. Invoke $token -operation setprop to set the security properties for the connection.
4. Invoke $token -operation list to get the list of mechanisms to send to the client.
5. When client selects a mechanism, invoke $token -operation start, and go to Step 7.
6. Invoke $token -operation step.
7. Look at the result:

   > normal return:
   >   Tell the client it's successful and goto Step 8.
   > continue return:
   >   Send the result to the client, wait for more input, and goto Step 6.
   > error return:
   >   Tell the client it loses and abort.

8. Invoke $token -operation getprop to get the new security properties for the connection.

Now, let's look at the actual procedures.

    sasl::server_init     \
        -callbacks list   \
       ?-appname   string?

Initialize server-side code, supplying default -callbacks and an -appname for logging.

    sasl::server_new          \
        -service      string  \
       ?-serverFQDN   string? \
       ?-realm        string? \
       ?-iplocalport  string? \
       ?-ipremoteport string? \
       ?-callbacks    script? \
       ?-flags        list?

Return a "server token" for a single SASL connection, supplying the registered name of the -service. To find out what flags are known, try sasl::info servernew_flags.

    $token -operation list    \
          ?-user      string?

Return a list of mechanisms available to the server.

    $token -operation start   \
           -mechanism string  \
           ?-input    string?

Start a -mechanism (optionally, with an -input string from the client), returning an output string for the client.

    $token -operation step   \
           -input     string

Perform another exchange, taking an input string from the client, returning an output string for the client.

    $token -operation checkpass \
           -user      string    \
           -pass      string

Determine if a plaintext password is valid; if not, throw an error.

    $token -operation userexists \
           -server    string     \
          ?-realm     string?    \
           -user      string

Determine if a user exists; it not, throw an error.

    $token -operation setpass \
           -user      string  \
          ?-newpass   string? \
          ?-oldpass   string? \
          ?-flags     list?

Set the password for a user; on failure, throw an error. To find out what flags are known, try sasl::info setpass_flags.

    $token -operation   auxprop_request \
           ?-properties list?

Request a set of auxiliary -properties (or reset the list if this switch is absent); on failure, throw an error.

    $token -operation auxprop_getctx

Return a "propctx token" containing the current auxiliary properties.

#### 3.2 Client-only calls

The calling sequence is:

1. Invoke sasl::client_init to load plugins.
2. On each outgoing connection, invoke sasl::client_new to get a client token.
3. Invoke $token -operation setprop to set the security properties for the connection.
4. When the server indicates which mechanisms are available invoke $token -operation start, and go to Step 6.
5. Invoke $token -operation step.
6. Look at the result:

   > normal return:
   >   If the result is empty, goto Step 8; otherwise, send the result to the server and goto Step 7
   > continue return:
   >   Send the result to the server, wait for more input, and goto Step 5.
   > error return:
   >   You lose, abort.

   (If you're familiar with the Cyrus SASL library API, you'll notice there is no "interaction" return — Tcl SASL handles this through a callback for you.)

7. Wait for the server's confirmation.
8. Invoke $token -operation getprop to get the new security properties for the connection.

Now, let's look at the actual procedures.

    sasl::client_init   \
        -callbacks list

Initialize client-side code, supplying default -callbacks.

    sasl::client_new          \
        -service      string  \
        -serverFQDN   string  \
       ?-iplocalport  string? \
       ?-ipremoteport string? \
       ?-callbacks    script? \
       ?-flags        list?

Return a "client token" for a single SASL connection, supplying the registered name of the -service, and the -serverFQDN of the server. To find out what flags are known, try sasl::info clientnew_flags.

    $token -operation  start \
           -mechanisms list  \
          ?-interact   script?

Select one of the given -mechanisms, and return a serialized array containing two elements: the mechanism to use, and, an output string for the server.

    $token -operation step   \
           -input     string \
          ?-interact  script?

Perform another exchange, taking an input string from the server, returning an output string for the server.

#### 3.3 Client/Server calls

    $token -operation info

Return a list of known operations for the token.

    $token -operation getprop \
           -property  string

Return a string corresponding to the value of the given -property. To find out what properties are known, try sasl::info getprops.

    $token -operation setprop \
           -property  string  \
           -value     string

Set the -value of the given -property. To find out what properties are known, try sasl::info setprops.

    $token -operation errdetail

Return the detail for the last error encountered for the token.

    $token -operation decode \
           -input     string

Take an -input string and decipher/verify it (using the previously-negotiated security layer), and return a plaintext string.

    $token -operation encode \
           -input     string

Take an -output string and encipher/sign it (using the previously-negotiated security layer), and return a string to be sent over the network.

#### 3.4 Miscellaneous calls

    sasl::decode64 string

Return a string decoded from base64.

    sasl::done

Release all resources associated with this package.

    sasl::encode64 string

Return a string encoded to base64.

    sasl::errstring
        -code      number  \
       ?-languages string?

Take a SASL result -code and a list of -languages (e.g., "en-US"), and return a serialized array containing two elements: the corresponding diagnostic string, and, optionally, the language used to localize the diagnostic.

    sasl::info ?option?

Return a list of known options, or a list of known choices for a given option.

    sasl::mechanisms

Return a list of all known mechanisms.

TOC

### 4. EXAMPLES

#### 4.1 Server

    proc sasl_log {data} {
        array set params $data

    # look at data(level) and data(message)
    }

    proc server_callback {id data} {
        array set params $data

    # look at data(level) and data(message)
        global server

        array set params $data

        switch -- $id {
            getopt {
                if {![info exists params(plugin)]} {
                    set params(plugin) ""
                }
                switch -- $params(plugin)/$params(option) {
                    /auto_transition
                        -
                    /canon_user_plugin
                        -
                    /mech_list
                        -
                    /sasldb_path
                        -
                    OTP/opiekeys
                        -
                    default {
    # if value isn't set, an error is thrown below
    # that's okay, the Cyrus SASL library will use a default value
                    }
                }
            }

            verifyfile {
    # set value...
            }

            proxy {
    # set value...
            }

            checkpass {
    # set value...
            }
        }

        return $value
    }

    sasl::server_init -callbacks [list [list log sasl_log]]

    set callbacks {}
    foreach id [list getopt verifyfile proxy checkpass] {
        lappend callbacks [list $id "server_callback $id"]
    }

    set token [sasl::server_new -service    $service   \
                                -callbacks  $callbacks \
                                -flags      [list success_data]]

    if {$ssf > 0} {
        $token -operation setprop ssf_external $ssf
    }

    if {[string length $clientID] > 0} {
        $token -operation setprop auth_external $clientID
    }

    $token -operation setprop sec_props \
           [list min_ssf     $min    \
                 max_ssf     $max    \
                 max_bufsize $bufsiz \
                 flags       $flags]

    set mechlist [$token -operation list]

    # send $mechlist to client
    # recv mechanism and input from client

    set code 4
    for {set operation start} {$code == 4} {set operation step} {
        set cmd [list $token -operation $operation \
                             -input     $input]
        if {![string compare $operation start]} {
            lappend cmd -mechanism $mechanism
        }

        switch -- [set code [catch { eval $cmd } output]] {
            0 {
    # send success to client

                set ssf [$token -operation getprop ssf]
              }

        4 {
    # send continue to client with output
    # recv input from client
        }

        default {
    # send error to client
        }
    }

#### 4.2 Client

    proc client_callback {data} {
        global client

        array set params $data

        switch -- $params(id) {
            pass
                -
            echoprompt
                -
            noechoprompt {
    # ask the user...
            }

            getrealm {
                if {[info exists params(available)]} {
    # ask the user to pick one from the list...
                } else {
    # ask the user to supply one...
                }
            }
        }

        return $value
    }

    proc client_interact {data} {
        global client

        array set params $data
        set id $params(id)

        catch { set value $params(default) }

    # ask the user to enter the $id using $params(prompt)...

        return $value
    }

    sasl::client_init -callbacks [list [list log sasl_log]]

    # interact script will supply these
    set callbacks [list authname cnonce language user]

    # callback script will supply these
    foreach id [list pass echoprompt noechoprompt] {
        lappend callbacks [list $id client_callback]
    }

    set token [sasl::client_new -service    $service   \
                                -serverFQDN $fqdn      \
                                -callbacks  $callbacks \
                                -flags      [list success_data]]

    if {$ssf > 0} {
        $token -operation setprop ssf_external $ssf
    }

    if {[string length $serverID] > 0} {
        $token -operation setprop auth_external $serverID
    }

    $token -operation setprop sec_props \
           [list min_ssf     $min    \
                 max_ssf     $max    \
                 max_bufsize $bufsiz \
                 flags       $flags]

    # recv mechlist from server

    switch -- [set code [catch { $token -operation  start     \
                                        -mechanisms $mechlist \
                                        -interact   client_interact } result]] {
        0 - 4 {
            array set data $result
    # send data(mechanism) and optionally data(output) to server
    # recv input from server
            set code 4
        }

        default {
    # error!
        }
    }

    while {$code == 4} {
        switch -- [set code [catch { $token -operation step   \
                                            -input     $input \
                                            -interact  client_interact
                                   } output]] {
            0 {
                if {![string compare $output ""]} {
                    break
                }
              }

            4 {
            }

            default {
    # error!
            }

    # make sure that server response says to continue...

    # send output to server
    # recv input from server
        }

    # make sure that server's last response says complete...
    }

TOC

### References

[1] | Myers, J., "Simple Authentication and Security Layer (SASL)", RFC 2222, October 1997.

TOC

### Author's Address

| Marshall T. Rose
| Dover Beach Consulting, Inc.
| POB 255268
| Sacramento, CA 95865-5268
| US
Phone: | +1 916 483 8878
Fax: | +1 916 483 8848
EMail: | mrose@dbc.mtview.ca.us

TOC

### Appendix A. TODO List

- Add routines for propctx tokens.
- Handle property_names and property_values in security properties.

TOC

### Appendix B. Copyrights

(c) 2002 Marshall T. Rose

Hold harmless the author, and any lawful use is allowed.

TOC

### Index

C |
| callbacks
| authname
| canonuser
| checkpass
| cnonce
| echoprompt
| getopt
| getpath
| getrealm
| language
| log
| noechoprompt
| pass
| proxy
| setpass
| user
| verifyfile
P |
| procedures
| client -operation start
| client -operation step
| sasl::client_init
| sasl::client_new
| sasl::decode64
| sasl::done
| sasl::encode64
| sasl::errstring
| sasl::info
| sasl::mechanisms
| sasl::server_init
| sasl::server_new
| server -operation auxprop_getctx
| server -operation auxprop_request
| server -operation checkpass
| server -operation list
| server -operation setpass
| server -operation start
| server -operation step
| server -operation userexists
| token -operation decode
| token -operation encode
| token -operation errdetail
| token -operation getprop
| token -operation info
| token -operation setprop