| | 119 | |
|---|
| | 120 | # Implements the SASL authd client protocol. |
|---|
| | 121 | # This is a very, very simple protocol that mimics the one used |
|---|
| | 122 | # by saslauthd and pwcheck, two outboard daemons included in the |
|---|
| | 123 | # standard SASL library distro. |
|---|
| | 124 | # The only thing this is really suitable for is SASL PLAIN |
|---|
| | 125 | # (user+password) authentication, but the SASL libs that are |
|---|
| | 126 | # linked into standard servers (like imapd and sendmail) implement |
|---|
| | 127 | # the other ones. |
|---|
| | 128 | # |
|---|
| | 129 | # You can use this module directly as a handler for EM Connections, |
|---|
| | 130 | # or include it in a module or handler class of your own. |
|---|
| | 131 | # |
|---|
| | 132 | # First connect to a SASL server (it's probably a TCP server, or more |
|---|
| | 133 | # likely a Unix-domain socket). Then call the #validate? method, |
|---|
| | 134 | # passing at least a username and a password. #validate? returns |
|---|
| | 135 | # a Deferrable which will either succeed or fail, depending |
|---|
| | 136 | # on the status of the authentication operation. |
|---|
| | 137 | # |
|---|
| | 138 | module SASLauthclient |
|---|
| | 139 | MaxFieldSize = 128*1024 |
|---|
| | 140 | |
|---|
| | 141 | def validate? username, psw, sysname=nil, realm=nil |
|---|
| | 142 | |
|---|
| | 143 | str = [username, psw, sysname, realm].map {|m| |
|---|
| | 144 | [(m || "").length, (m || "")] |
|---|
| | 145 | }.flatten.pack( "nA*" * 4 ) |
|---|
| | 146 | send_data str |
|---|
| | 147 | |
|---|
| | 148 | d = EM::DefaultDeferrable.new |
|---|
| | 149 | @queries.unshift d |
|---|
| | 150 | d |
|---|
| | 151 | end |
|---|
| | 152 | |
|---|
| | 153 | def post_init |
|---|
| | 154 | @sasl_data = "" |
|---|
| | 155 | @queries = [] |
|---|
| | 156 | end |
|---|
| | 157 | |
|---|
| | 158 | def receive_data data |
|---|
| | 159 | @sasl_data << data |
|---|
| | 160 | |
|---|
| | 161 | while @sasl_data.length > 2 |
|---|
| | 162 | len = (@sasl_data[0,2].unpack("n")).first |
|---|
| | 163 | raise "SASL Max Field Length exceeded" if len > MaxFieldSize |
|---|
| | 164 | if @sasl_data.length >= (len + 2) |
|---|
| | 165 | val = @sasl_data[2,len] |
|---|
| | 166 | @sasl_data.slice!(0...(2+len)) |
|---|
| | 167 | q = @queries.pop |
|---|
| | 168 | (val == "NO") ? q.fail : q.succeed |
|---|
| | 169 | else |
|---|
| | 170 | break |
|---|
| | 171 | end |
|---|
| | 172 | end |
|---|
| | 173 | end |
|---|
| | 174 | end |
|---|
| | 175 | |
|---|