Subject: Re: [kitten] sasl saml and sasl openID

From: Simon Josefsson <[email-masked]>
Date: Thu, 11 Nov 2010 14:12:48 +0100

Sam Hartman <[email-masked]> writes:

> Hi folks.  I think a couple people have read more into my comments
> Tuesday than I intended to make.

(I haven't listened to recordings or read jabber scripts yet..)

> As far as I know the OpenID and SAML mechanisms have the same security
> model as the web.  That means they are as good as things we use today on
> a regular basis.

That applies to (the current) OPENID + SAML20, but would not apply to
SAML20EC with channel bindings, right?

If you are saying that SAML20EC, with channel bindings, would not be
more secure than the weak security model provided to by the web (i.e.,
server-authentication at best), please elaborate.

> I'd prefer to push work beyond these mechanisms to a higher level of
> security within this working group.  That is all I intended to say.  I
> favor increasing security over time.

My hope is that experiments with OPENID+SAML20 will lead to specifying
changes for IdP/SP's that will allow them to also support channel
bindings, and thus become more secure.  SAML20EC could potentially be
the first to support this natively, but I'm hoping OPENID/SAML20 could
adopt something similar in an opt-in way.

/Simon

---

From: Sam Hartman <[email-masked]>
Date: Thu, 11 Nov 2010 22:12:58 -0500

>>>>> "Simon" == Simon Josefsson <[email-masked]> writes:

    Simon> That applies to (the current) OPENID + SAML20, but would not
    Simon> apply to SAML20EC with channel bindings, right?

Absolutely correct.

It's sounding a lot like it also applies to the oauth mech.

---

From: Klaas Wierenga <[email-masked]>
Date: Mon, 15 Nov 2010 11:06:53 +0100

On 11/12/10 4:12 AM, Sam Hartman wrote:

Sam,

> It's sounding a lot like it also applies to the oauth mech.

Why do you think sasl-saml could not support CB? I would think that in
principle the client could mix in some TLS stuff and as long as the IdP
is willing to carry that to the SP (coming in via the AuthN Request and
sent out in the AuthN statement)? Or am I missing something?
This would of course require the IdP to change, but as Scott is already
working on CB for SAML2 in OASIS that could be used in the future.

Klaas

---

From: Simon Josefsson <[email-masked]>
Date: Mon, 15 Nov 2010 12:08:34 +0100

> working on CB for SAML2 in OASIS that could be used in the future.

That would work -- but it is not a solution if the IdP cannot be
modified, and I thought initially this was the background for the SAML20
mechanism?  I'm happy to see that change, though, to get _optional_ CB
support.

One unpleasant aspect of this is that the client needs to be able to
insert its local channel binding into the SAML AuthN request received
from the server, so that the SP (and IdP) can check that the CB is
correct, and/or also be able to parse the SAML AuthN request to extract
the CB used by the server, so the client can check that the CB is
correct.  This will be possible to implement, but requires some SAML
knowledge to the otherwise SAML-unaware SASL implementation.

/Simon

---

From: Klaas Wierenga <[email-masked]>
Date: Mon, 15 Nov 2010 12:22:41 +0100

On 11/15/10 12:08 PM, Simon Josefsson wrote:

Simon,

> mechanism?  I'm happy to see that change, though, to get _optional_ CB

well, yes, not having to touch the IdP is a design goal. But I think we
all would like to see the overall security of the solution improve. So
if IdPs do support CB why not use it, so I would be happy to have an
optional CB.

> knowledge to the otherwise SAML-unaware SASL implementation.

Hmm yes. Does the CB data have to be inside the AuthN Request? Can it
not be concatenated to it instead? I.e. can the client not treat the
SAML request as a blob?

Klaas

---

From: Simon Josefsson <[email-masked]>
Date: Mon, 15 Nov 2010 13:27:13 +0100

> if IdPs do support CB why not use it, so I would be happy to have an

Agreed.

> not be concatenated to it instead? I.e. can the client not treat the

The problem then is how is the CB data cryptographically bound to the
SAML authentication?

For sake of thinking about it, consider if the TLS session between
client and server are anonymous and there is a MITM on it.  One way to
detect the MITM is to verify CB on the client and server side.  You
can't trust the TLS channel to provide data integrity of the CB data,
because of the MITM.

Putting the CB in the SAML negotiation is one obvious out-of-band (as
far as the TLS session is concerned) way to verify the CB.

I'm not saying there is no other way, just that I'm not seeing it.

Wild ideas could include deriving a key from the SAML authentication,
and using that to integrity check the CB.  However, since the client
presumably has no SAML knowledge, achieving that seems difficult.

Here is how it could work, in more detail:

1) The server adds the CB data in a SAML tag in the SAML AuthN request.

2) The client verify that the CB data is the same as it has locally.

   (Note, here a MITM could have replaced the CB data with something
   else, but that will detected later.)

3) The client sends the AuthN request off to the IdP, which copies the
   CB data into the SAML AuthN Statement.

4) The server verify that the CB data is the same as it sent.

   (This step will detect of there were a MITM.)

There is one potential problem with this: if the MITM is ALSO sitting
between the client and the IdP, it could replace the CB data, and
everything would look fine to both the client and server.  Thus, the
security considerations of this mechanism should describe this attack
and there should be a requirement that TLS with proper server
certificate validation MUST be used by the browser talking to the IdP.

/Simon

---

From: Sam Hartman <[email-masked]>
Date: Mon, 15 Nov 2010 10:09:12 -0500

The thing I'm most worried about here is how a client detects the
difference between an IDP that doesn't support CB and an attack.

---

From: Klaas Wierenga <[email-masked]>
Date: Mon, 15 Nov 2010 17:22:53 +0100

> difference between an IDP that doesn't support CB and an attack.

Ehm, I assume in our draft that the client configures the IdP and has an
existing trust relation with it, so it would be a matter of verifying
the server cert.

Klaas

---

From: Nicolas Williams <[email-masked]>
Date: Mon, 15 Nov 2010 11:29:16 -0600

> difference between an IDP that doesn't support CB and an attack.

CB must be non-negotiable if the app wants to use CB.

---

From: Martin Rex <[email-masked]>
Date: Mon, 15 Nov 2010 19:15:53 +0100 (MET)

> far as the TLS session is concerned) way to verify the CB.

I'm having serious difficulties to understand what is currently
discussed.   This could be because I do _not_ know how SAML works.

If you're talking about channel-bindings for GSS-API, then it is
about matching unique data supplied to gss_init_sec_context() by
the gss-api context initiator and supplied to gss_accept_sec_context()
by the gss-api context acceptor.

If you're talking about channel bindings provided by TLS, then
I'm thinking of the tls-unique CB established with the finished
handshake messages of the TLS handshake.

So if you want to protect the GSS-API authentication from your
client "C" to your server "S" with the help of channel bindings,
then the sequence of events need to be:

   C opens network connection with S
   C performs a complete TLS handhske with S
     => results in tls-unique channel bindings for both

   C calls gss_init_sec_context(CB=tls-unique-data)
      gss_init_sec_context() does whatever magic it needs to do
(**)  under the covers to bind the authentication to the supplied
      channel bindings -- while S is twiddling thumbs
   C performs GSS-API handshake with S
     => results in GSS-API authentication between C and S

If C has some secret keying material associated with that SAML thing,
then it might have some way to locally ensure the authentication is
cryptographically bound, otherwise it will have to start requesting
that SAML thingy during gss_init_sec_context() while both, Client
and Server are waiting.

-Martin

---

From: Martin Rex <[email-masked]>
Date: Mon, 15 Nov 2010 20:10:03 +0100 (MET)

Simon Josefsson wrote:
> [...]

If and whether a gssapi mechanism can make channel bindings optional or
negotiated depends on how exactly channel bindings are implemented.

The Kerberos 5 gssapi mechanism defines channel bindings in a fashion
that it can be fully optional, though not all (server) implementations
might treat it as optional.

  https://tools.ietf.org/html/rfc1964#page-4

In Kerberos 5 gssapi mechanism, the channel bindings are represented
by an MD5 hash over the channel bindings travelling in the checksum
of the authenticator of the AP_REQ.  The server, when unwrapping the
authenticator can see whether the client inserted channel bindings,
and needs to fail only if the acceptor application also supplied
channel binding information and those do not match.  If the acceptor
application did not provide channel bindings, the server implementation
of the gssapi mechanism could decide to simply ignore channel bindings
conveyed by the initiator.  But this option is only available
because the channel bindings are seperately transfered in an
integrity-protected fashion (in Kerberos, CB also happen to be
transferred under encryption, but that is an implementation detail
of the Kerberos mechanism--GSS-API is very explicit that mechanisms
are allowed to transfer CB data in the clear).

If a much simpler authentication scheme, like a password-based
challenge-response authentication, is wrapped with GSS-API then
it might not be possible to make gss-api channel bindings optional
(or require a prior unauthenticated negotiation of the channel
bindings prior to the first call of gss_init_sec_context()
by the initiator.

-Martin

---

From: Sam Hartman <[email-masked]>
Date: Mon, 15 Nov 2010 18:26:39 -0500

>>>>> "Nicolas" == Nicolas Williams <[email-masked]> writes:

    >> difference between an IDP that doesn't support CB and an attack.

    Nicolas> CB must be non-negotiable if the app wants to use CB.

OK, then I'd probably vote against supporting CB for SAML20.
My concern is that if we do, it will discourage apps from using CB
because it will significantly decrease their interoperability.

---

From: Nicolas Williams <[email-masked]>
Date: Mon, 15 Nov 2010 17:28:21 -0600

> because it will significantly decrease their interoperability.

What apps are we talking about?  Remember, SASL/GS2 does make CB
negotiable, while straight GSS-API makes it non-negotiable.

Nico
--

---

From: Martin Rex <[email-masked]>
Date: Tue, 16 Nov 2010 02:42:09 +0100 (MET)

> negotiable, while straight GSS-API makes it non-negotiable.

As I tried to explain here:
http://www.ietf.org/mail-archive/web/kitten/current/msg02121.html

it is definitely possible for GSS-API to use optional channel bindings,
i.e. negotiate the use of channel bindings.

You might, however, create a security problem for you protocol if
you outsource the protection of your network communication to some
other protocol instead of using GSS-API message protection primitives.

And one means to prevent the total disconnect of authentication from
the protection of the network communication is a concept called
"channel binding".  The GSS-API channel bindings facility is
a very specific subset of the "channel binding" concept.

In the original GSS-API design, there did not exist the idea of
gss-api mechanism that are authentication only or worse, out-sourcing
of message protection facilities to some other protocol.

Although I just noticed in the San Diego IETF meeting minutes March 1992
of the CAT working group the question:

  Discussion:
    . GSS-API:  for authentication only ?

but I also remember a presentation from Sanra Murphy (TIS) in the
CAT WG session on IETF 33 (July 1995 in Stockholm) and a very notica
suprize by many CAT fanciers about the very first GSS-API mechanism
that does not support message protection at all ... and a few resulting
clarifications to the GSS-API v2 I-Ds.

   http://tools.ietf.org/html/draft-ietf-cat-fipsjjjgss-00

And as it's primary purpose of this PKI-based mechanism was unidirectional
authentication of the initiator to the target, that initial proposal
is completely silent about naming, in particular possible or anticipated
values for the target_name parameter of gss_init_sec_context().

But similar to SPKM and DEC DASS, the concept of X.509 identities
back then was nothing like hostbased service names.

As I've previously mentioned, during the last 14 years we have been
offering interoperability certification for third-party single sign-on
solutions provided as plug-n-play shared libraries based on GSS-API v2.
I've seen ~20 different SSO-solutions, 5 of them were implementations
of Kerberos 5,  two were completely proprietary and the rest based
on X.509/PKI.  Only the Kerberos implementations recognized the
hostbased service name nametype, all other implementations rejected
it upfront with "GSS_S_BAD_NAMETYPE".

And when you're doing distributed apps, then being able to identify
apps completely independent of hostnames facilitates a lot of things.
There could be easily be running 5 or 6 apps independently using the
same protocol on a single host (HumanResource,Financials,ProcessIntegration,
MaterialsManagement,SupplyChainManagement, plus seperate apps development,
integration and test systems of each) and alternative instances of the
same app on 10 more hosts for distributing load, background and
batch processing.  Most X.509 based authentication schemes use 3-way
or 4-way authentication exchanges (signed challenge/response), so
that all distributed instances of a single application can share the
PKI credential and it is irrelevant for the user to which particular
host and which particular port it connects as long as the user
knows what system it wants to talk to, e.g. "Human Resources",
and neither the "Human Resources (Test)", "Customer Support"
nor "Financial (apps development)" system.  Also the ACLs that
all AppServers of a single system use to talk among each other
are pretty short with only one single entry.

If you are using Kerberos, things get difficult and complicated,
because every seperate instance has to be managed individually
and every move of an application instance to a different host
requires updates to all ACLs.  And you still need to devise
some scheme to distinguish several different entities of apps
that are based on the same protocol on a single host.

When you adapt the IETF robustness principle to application design
for GSS-API then it works like this:
"be liberal in characteristics and implementation choices
 that gssapi mechanims may exhibit, be conservative in
 the GSS-API features that you require for interoperability."

and host-based service names are nowhere close.

-Martin

---

From: Nicolas Williams <[email-masked]>
Date: Mon, 15 Nov 2010 21:09:34 -0600

> other protocol instead of using GSS-API message protection primitives.

See, the whole point of channel binding is to "outsource the protection
of your network communication ...".

Nico
--

---

From: Sam Hartman <[email-masked]>
Date: Mon, 15 Nov 2010 22:32:21 -0500

>> 
    >> 
    >> OK, then I'd probably vote against supporting CB for SAML20.  My
    >> concern is that if we do, it will discourage apps from using CB

    Nicolas> What apps are we talking about?  Remember, SASL/GS2 does
    Nicolas> make CB negotiable, while straight GSS-API makes it
    Nicolas> non-negotiable.

Well, SASL for one.  Yes, it's negotiable, but the obvious
implementation of a GS2 bridge would assume that if the mechanism can
support it, the environment will support it.

I'm arguing against the complexity of introducing GSS mechanisms that
support CB in environment-dependent manners.

---

From: Sam Hartman <[email-masked]>
Date: Mon, 15 Nov 2010 22:41:54 -0500

You've certainly seen a lot of GSS-API mechanisms.  However those
mechanisms were not proposed for standardization within the IETF.  I
suspect several of them may have been designed explicitly to meet SAP's
constraints, not to be used as general mechanisms on the Internet.  I
suspect none of them (with the exception of the Kerberos mechanisms)
were intended to work with SASL applications, SSH, or NFS.

However, by its charter, kitten must support SASL's needs.
I think it makes a lot of sense for kitten to support the needs of ssh
and nfs.

    Martin> When you adapt the IETF robustness principle to application
    Martin> design for GSS-API then it works like this: "be liberal in
    Martin> characteristics and implementation choices that gssapi
    Martin> mechanims may exhibit, be conservative in the GSS-API
    Martin> features that you require for interoperability."

    Martin> and host-based service names are nowhere close.

Hi.  In general, I value your input.  The above input might have been a
great thing to say to the SASL community 5 or 10 years ago.  It might
still be a valid thing to say to SASL in terms of trying to improve
naming of SASL applications if you can come up with constructive ways of
dealing with the UI challenges of today's SASL applications and meeting
those principals.

However, restating this same position again and again without responding
to the issues we're bringing up is not constructive.

If you ever get to a point where you want to understand the SASL
constraints and propose solutions within those constraints (possibly
along with evolutionary changes to those constraints) I'd welcome your
input.

Until then, all I can do is hope that you are in the rough when
consensus is called.

---

From: Nicolas Williams <[email-masked]>
Date: Mon, 15 Nov 2010 21:46:08 -0600

> support it, the environment will support it.

Oh, I see the issue.  You're talking about whether the infrastructure
supports CB, not whether the mechanism itself does.  Is there any way to
inquire?  (I guess we'd need to inquire on the initiator credential.)

> support CB in environment-dependent manners.

Yes, sorry I'd missed that.  I agree.

---

From: Martin Rex <[email-masked]>
Date: Tue, 16 Nov 2010 07:39:44 +0100 (MET)

Sam Hartman wrote:
> constraints, not to be used as general mechanisms on the Internet.

I don't think that our constraints were that unusual for
a an existing distributed application were GSS-API support
is added in a backwards-compatible fashion into existing
communication protocols. 

(True Single Sign-On, transfer of security contexts between
 processes (gss_export/import_sec_context), canonical identities
 for users and servers, named initiating and accepting credentials
 for the server, default initiating credentials for the client,
 and fairly moderate "performance" limits for gssapi calls and
 limits to the context token sizes and message size increase
 for message protection calls.)

>
> I suspect none of them (with the exception of the Kerberos mechanisms)

But that is would be a failure of SASL, SSH and NFS rather than
of GSS-API mechanisms.  DEC DASS, fipsjjjgss, spkm (rfc2025),
and DCE's secure directory lookup existed _long_ before these
protocols chose to adopt GSS-API.

Protocols which got GSS-API support wrongs suffered from a severe
Kerberos inbreed problem.  (If your only tool is a hammer then
everything looks like a nail to you).

For SSH supporting arbitrary server GSS-API names as alternatives to
server public keys in your ~/.ssh/known_hosts file should be easy,
obvious and straightforward.

Similar for NFS, supporting arbitrary GSS-API target names
as mount options.

Such fairly minor but important changes would make those protocols
portable in the way GSS-API defines it, and make them work with
all of the GSS-API mechanisms that I have encountered.

Hash algorithm agility or Unicode support are a magnitude more
difficult to achieve -- yet those do matter to the IESG, whereas
there appears to be an IESG blind spot for GSS-API mechanism /
GSS-API naming agility.

> I think it makes a lot of sense for kitten to support the needs of ssh

I don't think it is sensible to artificially limit kitten work
to obvious defects in those specs that can be fixed along the way
with little effort.

I did ask several times for providing configurability
of arbitrary target names in NFSv4 when it the relevant I-D
was announced on the CAT mailing list for discussion/input
(by IIRC Mark Eisler),

> to the issues we're bringing up is not constructive.

I have been trying to provide feedback and input to usability questions
that were posted on cat-ietf, krb-wg and kitten during the last 10 years,
but I didn't have time to actively track all other IETF working groups
or provide complete counter-proposals every time that some app
protocol took deliberate inappropriate short-cuts with respect
to GSS-API portability.

> along with evolutionary changes to those constraints) I'd welcome your

When you don't care about the peer's identity, the communication target
enpoint can be described with
  (ip-address, protocol, port-number)

When you want to authenticate the peer, the communication target
endpoint can be described with
  (ip-address, protocol, port-number, peer-identity)

I don't think it requires huge amounts of rocket science to account
for peer-identities that are independent from network endpoint
addresses when adding capabilities for securely authenticating
connection acceptors to connection initiators to a protocol
that uses GSS-API rather than native Kerberos.

If an application is given the peer-identity seperately, then
all security problem with insecure transforms on the representation
of the communication endpoint disappear.  It no longer matters whether
that is supplied as a hostname, whether that hostname translates
to a CNAME before it translates to an A record, to what name
the reverse DNS lookup matches, whether a netbios/WINS lookup
is used instead, whether a lookup uses the local hosts file
rather than DNS or wether the user supplies an unqualified
hostname or a full host.f.q.d.n or host.f.q.d.n. with
a trailing dot (.) tacked on the end.

Running multiple instances of the same application on a host
with distinct security properties and distinct server identities
also works in an obvious and straightforward fashion.

-Martin

---

From: Martin Rex <[email-masked]>
Date: Thu, 18 Nov 2010 03:41:38 +0100 (MET)

Sam Hartman wrote:
> mechanisms were not proposed for standardization within the IETF.

Actually, it is just the opposite.

  http://tools.ietf.org/html/draft-ietf-cat-kerb5gss   (->rfc1964)
and
  http://tools.ietf.org/html/draft-ietf-cat-spkmgss    (->rfc2025)

have about the same age.

rfc-2743, section 5.3
  http://tools.ietf.org/html/rfc2743#page-90
was already part of the very first GSS-API v2 draft
  http://tools.ietf.org/html/draft-ietf-cat-gssv2-00

and described the architecture of a real implementation/product
that we certified for interop with our app in 1998.

So the architecture for X.509/PKI gssapi mechanism related to SPKM
existed at least one year before I first heard about the existence
of GSS-API.

During the design of the GSS-API support by our application, I tried
to accomodate for the _published_ characteristics, because it was clear
that we were _not_ going to build and ship Single Sign-On solutions
ourselves, but instead rely solely on third-party products and
therefore be portable as much as our own architecture allowed.

Adding an addtional target-identity paramter to all connection APIs,
command lines and configurations was actually trivial compared to the hoops
we had to jump through in order to account for Kerbgss shortcomings.

The non-obvious Kerberos 5 gssapi mechanism problems:

    - enforced security context expiration.  Our app is the only app I know
      that implements a security context refresh logic in order
      to replace an expiring gssapi security context with a new
      one for longterm communication links

      For a very short time in Windows2000 Beta3 Microsoft enabled
      security context expiration and realized that they couldn't
      handle it, so they are ignoring it ever since.

      Some SPKM mechanisms offer lifetime-limited security contexts
      at request by the application.

    - initiating credentials expiration requiring frequent out-of-band
      refresh operations.  The side-effecting of credentials lifetime
      on security context lifetime is a challenge for automatic
      security context refresh logic.

      Other than TLS, security context refresh is burdened completely
      onto the caller above GSS-API, who has to establish a seperate
      security context, check whether the lifetime of the new is
      longer than the remaining of the old and then synchronously
      change from using the old security context for message protection
      and unprotection to using the new security context and deleting
      the old.

      Expiring initiating credentials required admins of distributed apps
      to configure e.g. a frequent cron-job "kinit -k app-abc/host.f.q.d.n"
      to refresh initiating creds, because many kerb-libraries on unix
      didn't simply do this themselves and didn't document this necessity
      anywhere either.  Microsoft Kerberos was the first Kerberos5
      implementation I saw with an acceptable credentials management.

    - users can not acquire accepting credentials
      With SPKM you can initiate trivially in both directions, while
      with Kerberos5, whenever you need a parallel,independent,
      gss-api protected communication link established, you need to
      ensure that the original initiator will also be the initiator
      for all additional parallel communication links.

      With SPKM, no such restriction exists, so an acceptor can
        gss_inquire_context(old_ctx, &src_name, &targ_name);
        gss_acquire_cred(&acc_cred, &targ_name);
        gss_init_sec_context(cred=acc_cred, target=src_name, &new_ctx)
      with ease, while with Kerberos, the app must first define
      and transfer an app request over old_ctx telling the initiator
      to establish a new security context.

      Microsoft Kerberos allows users to acquire accepting credentials.
      Since there is some security problem with allowing rfc-1964 two-token
      authentication with acceptors using password-based keys, Microsoft
      also implemented a (so far not standardized) variant of the
      Kerberos 5 mechanism for the "user2user" or "double-TGT" authentication
      scheme from the Kerberos 5 protocol specification.

    - Kerberos does not work across firewalls and alien address spaces
      Our app client was/is able to traverse proxy chains, e.g. in order
      to access customer systems for remote support.  Securing such logons
      with Kerberos will never work.  There is a proposal for an
      "IAKERB" gssapi mechanism from Microsoft to "out-source" all
      communication between the initiator/client and the KDC to the
      acceptor/server, but there hasn't been much visible progress over
      the last decade.

      SPKM mechanism implementations without a prerequisite of KDC-like
      access to an online directory (as Entrusts product) work just fine
      across any number of app-level proxies and completely independent
      of the low-level network connectivity between initiator and acceptor.

Having to accomodate for the lack of hostbased service names
in your app and app protocol is really a no-brainer.

Accounting for undocumented shortcoming of most Kerberos implementations
in a large distributed application, on the other hand, requires quite
an enormous effort.

I do remeber our first Video Conference with MIT & Ted Ts'o in April 1995,
when we discussed how to make Kerberos Single Sign-On usable for our
application, and I proposed to use GSS-API to interface to MIT Kerberos.

Ted was very explicit that using the native Kerberos API would
be much easier for us -- but I alread had obtained a requirement
to support an X.509/PKI based authentication scheme for specific
other customers, so I chose GSS-API as a common ground for a
unified solution architecture.  And then Ted got me involved
with the IETF.

-Martin

---

From: Sam Hartman <[email-masked]>
Date: Mon, 22 Nov 2010 11:13:25 -0500

>>>>> "Klaas" == Klaas Wierenga <[email-masked]> writes:

    >> difference between an IDP that doesn't support CB and an attack.

    Klaas> Ehm, I assume in our draft that the client configures the IdP
    Klaas> and has an existing trust relation with it, so it would be a
    Klaas> matter of verifying the server cert.

OK.
If you're willing to configure that the IDP always supports or never
supports CB, that would work.

---

From: Nicolas Williams <[email-masked]>
Date: Mon, 22 Nov 2010 11:12:58 -0600

Agreed.