summaryrefslogtreecommitdiffstats
path: root/doc/wiki/AuthDatabase.Lua.txt
blob: 41caf61f3d5159989086d7b49caae64a350656d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
Lua based authentication
========================

Since v2.3.0 you can implement passdb and userdb using Lua
[https://www.lua.org/] script.

Contents


 1. Lua based authentication

     1. Known bugs

     2. Lua interface

     3. Auth request methods

     4. Password database

     5. User database

     6. Examples

Known bugs
----------

 * Before 2.3.4 when returning a table with values, the table values are
   mistakenly converted into a number if they seem like a number. So if you are
   using values like '012345', this would get converted into '12345'
 * Before 2.3.4 returning password without scheme would cause a crash.

Lua interface
-------------

For details about Dovecot Lua, see <Design.Lua.txt>.

When used in authentication, additional module *dovecot.auth* is added, which
contains constants for passdb and userdb.

List of constants
-----------------

 * dovecot.auth.PASSDB_RESULT_INTERNAL_FAILURE
 * dovecot.auth.PASSDB_RESULT_SCHEME_NOT_AVAILABLE - indicates password scheme
   that cannot be understood
 * dovecot.auth.PASSDB_RESULT_USER_UNKNOWN
 * dovecot.auth.PASSDB_RESULT_USER_DISABLED
 * dovecot.auth.PASSDB_RESULT_PASS_EXPIRED
 * dovecot.auth.PASSDB_RESULT_NEXT - indicates that this passdb did not
   authenticate user, next passdb should do it
 * dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH
 * dovecot.auth.PASSDB_RESULT_OK
 * dovecot.auth.USERDB_RESULT_INTERNAL_FAILURE
 * dovecot.auth.USERDB_RESULT_USER_UNKNOWN
 * dovecot.auth.USERDB_RESULT_OK

Also, it registers object *struct auth_request** which lets access various
parts of the auth request. You should use the loggers associated with
auth_request when possible.

Auth request methods
--------------------

Functions:

 * auth_request#log_debug(text) - logs debug message (if debug is enabled, noop
   otherwise)
 * auth_request#log_error(text) - logs error message
 * auth_request#log_info(text) - logs informational message
 * auth_request#log_warning(text) - logs warning message
 * auth_request#response_from_template(template) - takes in key=value template
   and expands it using var_expand and produces table suitable for passdb
   result
 * auth_request#var_expand(template) - performs var expansion on the template
   using <Variables.txt>
 * auth_request#password_verify(crypted_password, plain_password) - checks if
   the plain password matches the crypted or hashed password
 * auth_request#event() - Returns child event for the auth request, can be used
   for logging and other events. Comes with a prefix. (Since v2.3.6+)

Subtables:

 * auth_request#passdb
 * auth_request#userdb

Members:

See <Variables.txt> for details

 * auth_request#auth_domain
 * auth_request#auth_user
 * auth_request#auth_username
 * auth_request#cert
 * auth_request#client_id
 * auth_request#domain
 * auth_request#domain_first
 * auth_request#domain_last
 * auth_request#home
 * auth_request#lip
 * auth_request#local_name
 * auth_request#login_domain
 * auth_request#login_user
 * auth_request#login_username
 * auth_request#lport
 * auth_request#master_user
 * auth_request#mech
 * auth_request#orig_domain
 * auth_request#orig_user
 * auth_request#orig_username
 * auth_request#password
 * auth_request#pid
 * auth_request#real_lip
 * auth_request#real_lport
 * auth_request#real_rip
 * auth_request#real_rport
 * auth_request#rip
 * auth_request#rport
 * auth_request#secured
 * auth_request#service
 * auth_request#session
 * auth_request#session_pid
 * auth_request#user
 * auth_request#username

Additionally you can access

 * skip_password_check - Set if the password has already been validated by
   another passdb
 * passdbs_seen_user_unknown - If some previous passdb has not found this user
 * passdbs_seen_internal_failure - If some previous passdb has had internal
   failure
 * userdbs_seen_internal_failure - If some previous userdb has had internal
   failure

Password database
-----------------

Lua passdb supports two modes of function. It can behave as lookup database, or
password verification database.

Lookup function signature is *auth_passdb_lookup(request)* and the password
verification signature is *auth_password_verify(request, password)*

Both functions must return a tuple, which contains a return code, and also
additionally string or table. Table must be in key-value format, it will be
imported into auth request. The string must be in key=value format, except if
return code indicates internal error, the second parameter can be used as error
string.

If *auth_verify_password* is found, it's always used.

To configure passdb in dovecot, use

---%<-------------------------------------------------------------------------
passdb {
   driver = lua
   args = file=/path/to/lua blocking=yes # default is yes
}
---%<-------------------------------------------------------------------------

By default, dovecot runs Lua scripts in auth-worker processes. If you do not
want this, you can disable blocking, and Lua script will be ran in auth
process. This can degrade performance if your script is slow or makes external
lookups.

User database
-------------

Lua userdb supports both single user lookup and iteration. Note that iteration
will hold the whole user database in memory during iteration.

User lookup function signature is *auth_userdb_lookup(request)*. The function
must return a tuple, which contains a return code, and also additionally string
or table. Table must be in key-value format, it will be imported into auth
request. The string must be in key=value format, except if return code
indicates internal error, the second parameter can be used as error string.

User iteration function signature is *auth_userdb_iterate*, which is expected
to return table of usernames. Key names are ignored.

To configure userdb in dovecot, use

---%<-------------------------------------------------------------------------
userdb {
   driver = lua
   args = file=/path/to/lua blocking=yes # default is yes
}
---%<-------------------------------------------------------------------------

Examples
--------

Skeleton
--------

---%<-------------------------------------------------------------------------
function auth_passdb_lookup(req)
  if req.user == "testuser1" then
    return dovecot.auth.PASSDB_RESULT_OK, "password=pass"
  end
  return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "no such user"
end

function auth_userdb_lookup(req)
  if req.user == "testuser1" then
    return dovecot.auth.USERDB_RESULT_OK, "uid=vmail gid=vmail"
  end
  return dovecot.auth.USERDB_RESULT_USER_UNKNOWN, "no such user"
end

function script_init()
  return 0
end

function script_deinit()
end

function auth_userdb_iterate()
  return {"testuser1"}
end
---%<-------------------------------------------------------------------------

Simple username password database (such as opensmtpd)
-----------------------------------------------------

The example uses whitespace separated username and password. As a special
caution, the way Lua is used here means you can have multiple user password per
line, instead of just one.This can be extended to more complicated separators
or multiple fields per user.

If you only want to autenticate users, and don't care about user listing, you
can use

---%<-------------------------------------------------------------------------
function auth_passdb_lookup(req)
    for line in io.lines("/path/to/file") do
        for user, pass in string.gmatch(line, "(%w+)%s(.+)") do
            if (user == req.username) then
                -- you can add additional information here, like userdb_uid
                return dovecot.auth.PASSDB_RESULT_OK, "password=" .. pass
            end
        end
    end
    return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, ""
end
---%<-------------------------------------------------------------------------

If you also want to be able to list users, so that you could use doveadm cmd -A

---%<-------------------------------------------------------------------------
local database = "/path/to/file"

function db_lookup(username)
    for line in io.lines(database) do
        for user, pass in string.gmatch(line, "(%w+)%s(.+)") do
            if (user == username) then
                return {result=0, password=pass}
            end
        end
    end
    return {result=-1}
end

function auth_passdb_lookup(req)
    res = db_lookup(req.username)
    if res.result == 0 then
        -- you can add additional information here for passdb
        return dovecot.auth.PASSDB_RESULT_OK, "password=" .. res.password
    end
    return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, ""
end

function auth_userdb_lookup(req)
    res = db_lookup(req.username)
    if res.result == 0 then
        -- you can add additional information here for userdb, like uid or home
        return dovecot.auth.USERDB_RESULT_OK, "uid=vmail gid=vmail"
    end
    return dovecot.auth.USERDB_RESULT_USER_UNKNOWN, ""
end

function auth_userdb_iterate()
   users = {}
   for line in io.lines(database) do
        for user in string.gmatch(line, "(%w+)%s.+") do
            table.insert(users, user)
        end
   end
   return users
end
---%<-------------------------------------------------------------------------

(This file was created from the wiki on 2019-06-19 12:42)