]>
Commit | Line | Data |
---|---|---|
14f8443b | 1 | diff -ur courier-imap-1.5.3-orig/authlib/README.authmysql.myownquery courier-imap-1.5.3/authlib/README.authmysql.myownquery |
2 | --- courier-imap-1.5.3-orig/authlib/README.authmysql.myownquery Tue Jan 8 06:01:22 2002 | |
0e7557ae | 3 | +++ courier-imap-1.5.3/authlib/README.authmysql.myownquery Mon Oct 14 01:05:11 2002 |
14f8443b | 4 | @@ -2,13 +2,18 @@ |
5 | ||
6 | ||
7 | ||
8 | - Developer Notes for courier-imap-myownquery.patch | |
0e7557ae | 9 | |
14f8443b | 10 | + Developer Notes and Usage Instructions |
11 | + | |
12 | + of | |
13 | + | |
14 | + courier-imap-authmysql-myownquery | |
15 | ||
0e7557ae | 16 | + by |
17 | + | |
18 | + Pawel Wilk <siefca@kernel.pl> | |
14f8443b | 19 | |
20 | ||
14f8443b | 21 | - document version: 1.03 |
22 | - author: Pawel Wilk | |
23 | ||
24 | ||
25 | ||
0e7557ae | 26 | @@ -19,75 +24,843 @@ |
27 | ||
28 | ||
14f8443b | 29 | |
0e7557ae | 30 | + .. table of contents.. |
14f8443b | 31 | |
32 | ||
0e7557ae | 33 | +PREAMBLE |
34 | ||
14f8443b | 35 | +PART I - Usage Instructions |
36 | ||
37 | + 1 What's that? | |
38 | + | |
39 | + 2 When will I need it? | |
40 | + | |
41 | + 3 How does it work? | |
42 | + 3.1 configuration variables | |
43 | + 3.2 queries | |
44 | + 3.3 substitutions | |
45 | + 3.4 triggers | |
0e7557ae | 46 | + 3.5 empty default domain name |
47 | + 3.6 whitespaces in queries | |
14f8443b | 48 | + |
49 | + 4 Examples of usage | |
50 | + 4.1 corporate mail system | |
51 | + 4.1.1 database structure | |
52 | + 4.1.2 authdaemon configuration | |
53 | + 4.2 virtual mail domains provider | |
54 | + 4.2.1 database structure | |
55 | + 4.2.2 authdaemon configuration | |
56 | + | |
57 | +PART II - Developer Notes | |
58 | + | |
59 | + 1 Modifications overview | |
60 | + | |
61 | + 2 Definitions | |
62 | + | |
63 | + 3 New data types | |
64 | + 3.1 struct var_data | |
65 | + 3.2 typedef size_t (*parsefunc) | |
66 | + | |
67 | + 4 New functions | |
68 | + 4.1 get_variable | |
69 | + 4.2 parse_core | |
70 | + 4.3 ParsePlugin_counter | |
71 | + 4.4 ParsePlugin_builder | |
72 | + 4.5 parse_string | |
73 | + 4.6 validate_password | |
74 | + 4.7 get_localpart | |
75 | + 4.8 get_domain | |
76 | + 4.9 get_username | |
77 | + 4.10 parse_select_clause | |
78 | + 4.11 parse_chpass_clause | |
79 | + 4.12 auth_mysql_on_trigger | |
0e7557ae | 80 | + 4.13 auth_mysql_on_pass |
81 | + 4.14 auth_mysql_checkpassword | |
14f8443b | 82 | |
83 | + 5 Ideas and TODO | |
84 | ||
14f8443b | 85 | -0 What's that? |
0e7557ae | 86 | + 6 Thanks |
14f8443b | 87 | |
88 | -1 Modifications overview | |
89 | ||
90 | -2 Definitions | |
91 | ||
92 | -3 New data types | |
93 | - 3.1 struct var_data | |
94 | - 3.2 typedef size_t (*parsefunc) | |
95 | - | |
96 | -4 New functions | |
97 | - 4.1 get_variable | |
98 | - 4.2 parse_core | |
99 | - 4.3 ParsePlugin_counter | |
100 | - 4.4 ParsePlugin_builder | |
101 | - 4.5 parse_string | |
102 | - 4.6 validate_password | |
103 | - 4.7 get_localpart | |
104 | - 4.8 get_domain | |
105 | - 4.9 parse_select_clause | |
106 | - 4.10 parse_chpass_clause | |
107 | - | |
108 | -5 Ideas and TODO | |
0e7557ae | 109 | |
110 | -6 Thanks | |
111 | ||
112 | ||
113 | +//////////////////////////////// PREAMBLE ///////////////////////////////////// | |
14f8443b | 114 | + |
0e7557ae | 115 | +This is README document for "myownquery" patch for Courier's Authdaemon. |
116 | +This document version is 1.36 | |
117 | + | |
118 | +* The patch, which this document describes is developed for Courier-IMAP | |
119 | + version 1.5.3 and the official patch revision is 2. | |
120 | + | |
121 | +* You can download the patch from the FTP server using URI: | |
14f8443b | 122 | + |
0e7557ae | 123 | +ftp://ftp.pld.org.pl/people/siefca/patches/courier/courier-imap-1.5.3-myownquery.patch |
14f8443b | 124 | |
0e7557ae | 125 | + it should also be accessible on mirroring servers, which list can be |
126 | + obtained under: http://www.pld.org.pl/ | |
127 | + | |
128 | +* To know more about getting Courier see http://www.courier-mta.org/ | |
129 | + | |
130 | +* This patch, including the documentation, is released under GNU GPL | |
131 | + license terms. You should look at the COPYING file present in | |
132 | + Courier sources. | |
133 | + | |
134 | +. | |
135 | +. | |
136 | +. | |
137 | +. | |
138 | +. | |
139 | +. | |
140 | + | |
141 | +//////////////////////////////// PART I - Usage /////////////////////////////// | |
142 | ||
143 | *----------------------- | |
144 | - 0 What's that? | |
145 | + 1 What's that? | |
146 | *----------------------- | |
147 | ||
148 | -Courier-imap-myownquery.patch allows administrator to set own MySQL queries | |
149 | -used by authdaemon to authenticate user (including fetchig credentials) and to | |
150 | -change user's password. It allows to construct SELECT or UPDATE clause in the | |
151 | -configuration file (authmysqlrc) by adding two new configuration variables: | |
152 | -MYSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE. It may be useful in the mail | |
153 | -environments where there is such a need to have different database structure | |
154 | -and/or tables scheme than expected by authmysql module. | |
155 | +Courier-imap-myownquery's features allow the administrator to set his | |
156 | +own MySQL queries used by authdaemon to authenticate a user (including | |
157 | +fetchig his credentials) and to change the user's password. It allows | |
158 | +one to write a SELECT and UPDATE clause in the configuration file | |
159 | +(authmysqlrc) using the new configuration options. It may be useful in | |
160 | +mail environments where there is a need to have a different database | |
161 | +structure and/or tables scheme than expected by authmysql module. | |
162 | + | |
163 | +It also implements a small parsing engine for substitution of | |
164 | +variables which may appear in the SQL clauses, such as a username or a | |
165 | +domain. | |
14f8443b | 166 | |
0e7557ae | 167 | -It also implements a small parsing engine for substitution variables which |
168 | -may appear in the clauses and are used to put informations like username | |
169 | -or domain into the right place of a query. | |
170 | ||
171 | -This patch was created using `diff -Nur` on courier-imap-1.3.12 source. | |
14f8443b | 172 | |
173 | ||
174 | ||
14f8443b | 175 | + *----------------------- |
176 | + 2 When will I need it? | |
0e7557ae | 177 | + *----------------------- |
14f8443b | 178 | + |
0e7557ae | 179 | + o When you already have some MySQL database filled up with the data |
14f8443b | 180 | + and there is no chance to change the whole structure to make it |
0e7557ae | 181 | + work with a standard authmysql table. Typical situation is when |
182 | + all the | |
183 | + data required to authenticate a user is arranged in more than one | |
184 | + table. | |
14f8443b | 185 | + |
0e7557ae | 186 | + o When you have some great idea how to make the database structure |
14f8443b | 187 | + more efficient due to your needs and your requirements. |
188 | + | |
0e7557ae | 189 | + o When doing something 'by-myself' is in your style and you just want |
14f8443b | 190 | + to create your own database, just to feel the pleasure of doing |
191 | + something original. :) | |
0e7557ae | 192 | |
193 | ||
14f8443b | 194 | + |
195 | + | |
196 | + | |
0e7557ae | 197 | + *----------------------- |
14f8443b | 198 | + 3 How does it work? |
199 | + *----------------------- | |
200 | + | |
201 | +There are three things which the feature concerns: | |
202 | + | |
203 | +- fetching clauses from the configuration file | |
204 | +- doing substitution replacements inside of SQL clauses | |
205 | +- passing prepared query on to the mysql interface funtions | |
206 | + | |
207 | +3.1 configuration options | |
208 | + | |
0e7557ae | 209 | +You can apply your own MySQL queries using a set of the configuration |
210 | +options. The options you'll need to make the authmysql your slave | |
211 | +are: | |
14f8443b | 212 | + |
213 | +MYSQL_SERVER (required) | |
214 | +MYSQL_USERNAME (required) | |
215 | +MYSQL_PASSWORD (required) | |
216 | + | |
217 | + The server name, userid, and password used to log in. | |
218 | + | |
0e7557ae | 219 | +MYSQL_DATABASE (required) |
14f8443b | 220 | + |
221 | + The name of the MySQL database we will open. | |
222 | + | |
0e7557ae | 223 | +DEFAULT_DOMAIN (optional) |
14f8443b | 224 | + |
0e7557ae | 225 | + If DEFAULT_DOMAIN is defined, and someone tries to log |
226 | + in as 'user', we will look up 'user@DEFAULT_DOMAIN' | |
227 | + instead. | |
14f8443b | 228 | + |
0e7557ae | 229 | +USER_DOMAIN_SEPARATORS (optional) |
14f8443b | 230 | + |
231 | + This may contain the set of characters used by parsing | |
0e7557ae | 232 | + routines to split local part of the virtual mailbox |
233 | + name from the part which describes the domain name. If | |
234 | + it's not defined the set containing @% is assumed, so | |
235 | + the user can enter either: user@domain or user%domain | |
14f8443b | 236 | + |
237 | +MYSQL_SELECT_CLAUSE (required) | |
238 | +MYSQL_CHPASS_CLAUSE (required under some circumstances) | |
239 | + | |
0e7557ae | 240 | + These are the major options you should use. See 3.2 |
241 | + section for more info. | |
14f8443b | 242 | + |
0e7557ae | 243 | +ON_PASS_OK_CLAUSE (optional) |
244 | +ON_PASS_FAIL_CLAUSE (optional) | |
245 | +ON_PASS_CHANGE_CLAUSE (optional) | |
14f8443b | 246 | + |
0e7557ae | 247 | + These are used to do a MySQL query whether user has |
248 | + passed the authentication verification | |
249 | + (ON_PASS_OK_CLAUSE) or there was the authentication | |
250 | + failure (ON_PASS_FAIL_CLAUSE), or whether user has | |
251 | + changed his password (ON_PASS_CHANGE_CLAUSE). | |
14f8443b | 252 | + Query results have no meaning. You can use the same |
253 | + substitution variables in your query as with | |
254 | + MYSQL_SELECT_CLAUSE. See 3.4 section for more info. | |
255 | + | |
256 | +The options which have no effect, and may be safetly left blank are: | |
257 | + | |
258 | +MYSQL_USER_TABLE | |
259 | +MYSQL_CRYPT_PWFIELD | |
260 | +MYSQL_CLEAR_PWFIELD | |
261 | +MYSQL_UID_FIELD | |
262 | +MYSQL_GID_FIELD | |
263 | +MYSQL_LOGIN_FIELD | |
264 | +MYSQL_HOME_FIELD | |
265 | +MYSQL_NAME_FIELD | |
266 | +MYSQL_MAILDIR_FIELD | |
267 | +MYSQL_QUOTA_FIELD | |
268 | +MYSQL_WHERE_CLAUSE | |
269 | + | |
270 | +3.2 queries | |
271 | + | |
0e7557ae | 272 | +The feature adds two configuration options (clauses), which are parsed |
273 | +first, and then applied as MySQL queries to MySQL interface | |
274 | +routines. These options are: MYSQL_SELECT_CLAUSE and | |
275 | +MYSQL_CHPASS_CLAUSE. After each option a number of spaces and/or tabs | |
276 | +is allowed, and then MySQL query is expected. For better look, your | |
277 | +queries can have line breaks. Each line break should be preceded by | |
278 | +the backslash sign. Look into examples chapter (4) to see how it | |
279 | +should look like. First clause is used to authenticate a user, and the | |
280 | +second to change his password. | |
14f8443b | 281 | + |
0e7557ae | 282 | +You should note that a query identified by MYSQL_SELECT_CLAUSE should |
283 | +return fixed number (9) of fields and each field should match the | |
284 | +variable expected by authentication routines. These fields are: | |
14f8443b | 285 | + |
14f8443b | 286 | + |
0e7557ae | 287 | +* username - which is the currently logged user's username (or the |
288 | + username with domain if you want it) | |
14f8443b | 289 | + |
0e7557ae | 290 | + cryptpw - which is the user's crypted password |
291 | +* | |
292 | + clearpw - which is the user's plaintext password | |
14f8443b | 293 | + |
0e7557ae | 294 | +* uid - which is a numerical UID value used as a process's UID when |
295 | + accessing the mailbox directory | |
14f8443b | 296 | + |
0e7557ae | 297 | +* gid - as above, but refers to GID |
14f8443b | 298 | + |
0e7557ae | 299 | +* home - which contains full path to the user's home directory |
14f8443b | 300 | + |
0e7557ae | 301 | + maildir - which contains the directory name inside the user's home; |
302 | + treated as INBOX folder when accessing mailbox - if it's | |
303 | + empty then the 'Maildir' string is used | |
14f8443b | 304 | + |
0e7557ae | 305 | +quota - which describes a quota size for the mailbox |
14f8443b | 306 | + |
0e7557ae | 307 | +fullname - which may contain the user's fullname |
14f8443b | 308 | + |
309 | +(The fields marked by the asterix sign are required and cannot have an | |
0e7557ae | 310 | + empty results. In case of passwords, at least one of the shown fields |
311 | + should contain some result.) | |
14f8443b | 312 | + |
313 | +So, the typical query clause may start with: | |
314 | + | |
315 | +MYSQL_SELECT_CLAUSE SELECT \ | |
316 | + users.username, \ | |
317 | + users.cryptpw, \ | |
318 | + users.clearpw, \ | |
319 | + domains.uid, \ | |
320 | + domains.gid, \ | |
321 | + users.mailbox_path) \ | |
322 | + '' \ | |
323 | + domains.quota, \ | |
324 | + '' \ | |
325 | +... | |
326 | + | |
327 | +Note that in this short example we're assuming that we have two tables | |
328 | +(users and domains) and INBOX path is always called 'Maildir' and | |
0e7557ae | 329 | +we're not using the fullname field (the query will always return an |
330 | +empty string in its place). | |
14f8443b | 331 | + |
0e7557ae | 332 | +Also note that you may discard one of the password fields if you don't |
333 | +want to use an authentication mechanism, which needs it. For example, | |
334 | +if you don't want to use MD5-CRAM you may put '' into the place of | |
335 | +clearpw (because, for example you're in paranoid mode and you don't | |
336 | +even want to keep plain passwords in the database:). | |
14f8443b | 337 | + |
338 | +3.3 substitutions | |
339 | + | |
0e7557ae | 340 | +Substitutions are strings, which may appear in your query, and which |
341 | +have a special meaning. You can also call them substitution | |
342 | +variables. If substitution variable is known for a clause context then | |
343 | +it is parsed. If it isn't known the error is generated. In the default | |
344 | +compilation of authmysql module any substitution variable is declared | |
345 | +inside of two substrings - the first is a dollar sign concatenated | |
346 | +with opening parenthesis, and the second is a closing parenthesis | |
347 | +sign. First symbol identifies beginning of a substitution variable, | |
348 | +and the second closes it. The string between the beginning and the | |
349 | +closing symbol is called substitution variable's name. | |
14f8443b | 350 | + |
351 | +When, as I said before, the name is known to the parsing routine the | |
0e7557ae | 352 | +substitution is made and the proper value appears in place of the |
353 | +substitution variable, while passing on the query for later | |
354 | +processing. | |
14f8443b | 355 | + |
356 | +Allowed substitution variables: | |
357 | + | |
0e7557ae | 358 | +context: MYSQL_SELECT_CLAUSE, ON_PASS_FAIL_CLAUSE, ON_PASS_OK_CLAUSE, |
359 | + ON_PASS_CHANGE_CLAUSE | |
14f8443b | 360 | + |
0e7557ae | 361 | +$(local_part) will be replaced by currently verified user's username |
14f8443b | 362 | + (without the domain part) |
363 | + | |
0e7557ae | 364 | +$(domain) will be replaced by currently verified user's domain |
365 | + name (if present, or if not present but the | |
366 | + DEFAULT_DOMAIN was used) or by the empty, | |
367 | + zero-length | |
14f8443b | 368 | + string if the domain cannot be obtained |
369 | + | |
0e7557ae | 370 | +$(username) will be replaced by currently verified user's username |
371 | + concatenated with the given domain name using | |
372 | + @ symbol -- if the domiain name cannot be | |
373 | + obtained (even looking up DEFAULT_DOMAIN) the | |
374 | + separation sign will not appear and only the | |
375 | + given username will be presented | |
14f8443b | 376 | + |
377 | +context: MYSQL_CHPASS_CLAUSE | |
378 | + | |
379 | +$(local_part) will be replaced by currently verified user's username | |
380 | + (without the domain part) | |
381 | + | |
382 | +$(domain) will be replaced by currently verified user's domain | |
383 | + name (if present, or if not present but the | |
384 | + DEFAULT_DOMAIN was used) or by the empty, zero-length | |
385 | + string if the domain cannot be obtained | |
386 | + | |
387 | +$(username) will be replaced by currently verified user's username | |
0e7557ae | 388 | + concatenated with the given domain name using |
389 | + @ symbol -- if the domiain name cannot be | |
390 | + obtained (even by looking up DEFAULT_DOMAIN) | |
391 | + the separation sign will not appear and only | |
392 | + the given username will be presented | |
14f8443b | 393 | + |
394 | +$(newpass) will be replaced by currently authenticated user's | |
395 | + new password to set up (plaintext password) | |
396 | + | |
397 | +$(newpass_crypt) will be replaced by currently authenticated user's | |
0e7557ae | 398 | + new password to set up (MD5 form created from |
399 | + entered plain form) | |
14f8443b | 400 | + |
401 | +3.4 triggers | |
402 | + | |
0e7557ae | 403 | +Triggers are MySQL queries, which are performed depending on |
404 | +authentication state. Currently, there are three triggers which you | |
405 | +may use. First is called ON_PASS_OK_CLAUSE and it is performed when | |
406 | +the authentication succeedes. The second is called | |
407 | +ON_PASS_FAIL_CLAUSE and has the reverse meaning. The third, which name | |
408 | +is ON_PASS_CHANGE_CLAUSE is performed whenever user has changed his | |
409 | +password. | |
410 | + | |
411 | +You can declare triggers in the authmysqlrc configuration file. They | |
412 | +can be used to arrange some logging facility in the database or just | |
413 | +to keep last times of the successful/failed login tries. The typical | |
414 | +trigger, which puts last login date into the users' table can look | |
415 | +like this: | |
14f8443b | 416 | + |
0e7557ae | 417 | +ON_PASS_OK_CLAUSE UPDATE users SET last_login=CURRENT_TIMESTAMP \ |
14f8443b | 418 | + WHERE username='$(username)'; |
419 | + | |
420 | +or, if you would like to know about last login failure for users you can try: | |
421 | + | |
0e7557ae | 422 | +ON_PASS_FAIL_CLAUSE UPDATE users SET last_bad_login=CURRENT_TIMESTAMP \ |
14f8443b | 423 | + WHERE username='$(username)'; |
0e7557ae | 424 | + |
425 | +and/or, if you want to know last password changes you can use: | |
426 | + | |
427 | +ON_PASS_CHANGE_CLAUSE UPDATE users SET pw_change=CURRENT_TIMESTAMP \ | |
428 | + WHERE username='$(username)'; | |
429 | + | |
14f8443b | 430 | +Note, that YOU CAN use the triggers even if you aren't using |
0e7557ae | 431 | +MYSQL_SELECT_CLAUSE. Also note, that if the entered username |
432 | +doesn't match any real user ON_PASS_FAIL_CLAUSE will be simply | |
433 | +discarded. To watch brute force attacs against known usernames | |
434 | +you have to use log files. ;] | |
435 | + | |
436 | +3.5 empty default domain name | |
437 | + | |
438 | +Sometimes happens, that you want to allow user to log in without | |
439 | +having a domain name entered and you expect it will be treated as an | |
440 | +empty string, neither an error, nor default domain. In that case you | |
441 | +should leave DEFAULT_DOMAIN option unset in authmysqlrc file and your | |
442 | +database should have empty (not NULL) string fields for users without | |
443 | +the domain name specified. | |
444 | + | |
445 | +3.6 whitespaces in queries | |
446 | + | |
447 | +In a few examples, here and in authmysqlrc file, I used to put many | |
448 | +whitespaces and tabs to make the examples more clear for reader. | |
449 | +However, it is recommended to not torture authdaemon's parser in | |
450 | +that way and to remove unnecessary characters. ;] | |
451 | + | |
452 | +For example, the clause: | |
453 | + | |
454 | +MYSQL_CHPASS_CLAUSE UPDATE \ | |
455 | + users \ | |
456 | + SET clearpw='$(newpass)', \ | |
457 | + cryptpw='$(newpass_crypt)' \ | |
458 | + WHERE username='$(local_part)' \ | |
459 | + AND domain_name='$(domain)' | |
460 | + | |
461 | +can be safetly rewritten as: | |
462 | + | |
463 | +MYSQL_CHPASS_CLAUSE UPDATE users \ | |
464 | +SET clearpw='$(newpass)', cryptpw='$(newpass_crypt)' \ | |
465 | +WHERE username='$(local_part)' AND domain_name='$(domain)' | |
466 | + | |
467 | + | |
468 | + | |
469 | + | |
470 | + | |
471 | + | |
14f8443b | 472 | + *----------------------- |
473 | + 4 Examples of usage | |
474 | + *----------------------- | |
475 | + | |
0e7557ae | 476 | +The "ownquery" feature gives you possibility to adapt an |
477 | +authentication query to the database. So the first thing you have to | |
478 | +do is to design the database structure you need, whithout being | |
479 | +grieved at what structure authentication routines like. You have to | |
480 | +take care about four essential things: | |
14f8443b | 481 | + |
482 | + o The database | |
483 | + | |
484 | + o The users' data in the database | |
485 | + | |
0e7557ae | 486 | + o The proper directories for keeping virtual mailboxes and a system |
487 | + user which can read and write them | |
14f8443b | 488 | + |
489 | + o The proper MySQL queries in your authmysqlrc configuration file | |
490 | + | |
491 | +4.1 corporate mail system | |
492 | + | |
493 | +This example is concerned about a corporate mail system with a small | |
0e7557ae | 494 | +ammount of served virtual domains. The database scheme was derived |
495 | +from tpop3d documentation and modified a bit. | |
14f8443b | 496 | + |
497 | +4.1.1 database structure | |
498 | + | |
499 | +Our goal here is to separate the data responsible for keeping mailbox | |
0e7557ae | 500 | +credentials from the data describing domains. |
14f8443b | 501 | + |
0e7557ae | 502 | +Let's create some tables for our example, filled up with an example |
503 | +data: | |
14f8443b | 504 | + |
505 | +table: domains | |
506 | + | |
507 | +purpose: associates virtual domain with domain name and informations | |
508 | + necessary to access mailboxes withing the domain | |
509 | + | |
0e7557ae | 510 | +fields: domain_name - fully qualified domain name |
511 | + path_prefix - absolute pathname which points to | |
512 | + a directory where domain's mailboxes | |
513 | + are located | |
514 | + quota - default quota for each mailbox | |
515 | + uid - UID used to work on mailboxes | |
516 | + gid - GID used to work on mailboxes | |
14f8443b | 517 | + |
518 | + +----------------+-------------+-----+-----+----------+ | |
519 | + | domain_name | path_prefix | uid | gid | quota | | |
520 | + +----------------+-------------+-----+-----+----------+ | |
521 | + | exampledom.com | /var/mail/x | 555 | 555 | 10000000 | | |
522 | + | pld.org.pl | /var/mail/p | 556 | 556 | 20000000 | | |
523 | + | pld.net.pl | /var/mail/p | 556 | 556 | 20000000 | | |
524 | + +----------------+-------------+-----+-----+----------+ | |
525 | + | |
526 | +table: users | |
527 | + | |
528 | +purpose: associates virtual mailbox with user and domain name, | |
529 | + and with informations necessary to access mailbox | |
530 | + | |
0e7557ae | 531 | +fields: username - user login name (mailbox name) |
532 | + domain_name - fully qualified domain name | |
533 | + mailbox_path - relative pathname for mailbox | |
534 | + (will be appended to the path_prefix | |
535 | + from domain_auth table to specify | |
536 | + user's mailbox location) | |
537 | + cryptpw - crypted password | |
538 | + plainpw - plaintext password | |
14f8443b | 539 | + |
540 | + +----------+----------------+--------------+------------+--------+ | |
541 | + | username | domain_name | mailbox_path | cryptpw | plainpw | | |
542 | + +----------+----------------+--------------+-----------+---------+ | |
543 | + | siefca | pld.org.pl | s/siefca | $1$fs45.. | dupa.8 | | |
544 | + | siefca | pld.net.pl | s/siefca | $1$fs45.. | dupa.8 | | |
545 | + | f00bar | exampledom.com | foobar | $1$g44w.. | secret | | |
546 | + +----------+----------------+--------------+-----------+---------+ | |
547 | + | |
0e7557ae | 548 | +Using MySQL monitor you can create these tables entering CREATE |
549 | +sequences. Be sure to connect to the database using administrative | |
550 | +MySQL account (usualy: mysql -u mysql -p). | |
14f8443b | 551 | + |
552 | +--------------------- cut here | |
553 | + | |
554 | +# Create the database called vmail. | |
555 | + | |
556 | +CREATE database vmail; | |
557 | + | |
0e7557ae | 558 | +# Create an example MySQL user, which can read, write and delete data |
559 | +# from vmail database. Username: vuser Password: secret_password | |
14f8443b | 560 | + |
561 | +GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.* | |
562 | + TO vuser@localhost | |
563 | + IDENTIFIED BY 'secret_password'; | |
564 | + | |
565 | +FLUSH PRIVILEGES; | |
566 | + | |
567 | +# Create the tables. | |
568 | + | |
569 | +use vmail; | |
570 | + | |
571 | +CREATE TABLE domains ( | |
572 | + domain_name char(255) DEFAULT '', | |
573 | + path_prefix char(255) DEFAULT '' NOT NULL, | |
574 | + uid int(10) unsigned DEFAULT '15000' NOT NULL, | |
575 | + gid int(10) unsigned DEFAULT '15000' NOT NULL, | |
576 | + quota char(255) DEFAULT '2000000' NOT NULL, | |
577 | + KEY domain_name (domain_name(255)) | |
578 | + ); | |
579 | + | |
580 | +CREATE TABLE users ( | |
581 | + username char(128) DEFAULT '' NOT NULL, | |
582 | + domain_name char(255) DEFAULT '', | |
583 | + mailbox_path char(255) DEFAULT '' NOT NULL, | |
584 | + cryptpw char(128) DEFAULT '' NOT NULL, | |
585 | + clearpw char(128) DEFAULT '' NOT NULL, | |
586 | + KEY username (username(128)) | |
587 | + ); | |
588 | + | |
589 | +# Create an example virtual domain entry | |
590 | +# name : exampledom.com | |
591 | +# uid : 555 | |
592 | +# gid : 555 | |
593 | +# path : /var/mail/x | |
594 | +# quota : 10 Megs per mailbox | |
595 | + | |
596 | +INSERT INTO domains VALUES ('exampledom.com', '/var/mail/x', 555, 555, | |
597 | + '10000000'); | |
598 | + | |
599 | +# Create an example virtual user entry | |
600 | +# username : siefca | |
601 | +# domain name : exampledom.com | |
602 | +# cryptpw : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/ | |
603 | +# clearpw : dupa.8 | |
604 | +# mailbox path : s/siefca | |
605 | + | |
606 | +INSERT INTO users VALUES ('siefca', 'exampledom.com', 's/siefca', | |
607 | + '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/', | |
608 | + 'dupa.8'); | |
609 | + | |
610 | +--------------------- cut here | |
611 | + | |
0e7557ae | 612 | +Note: If you would like to have your passwords more safe, then just |
613 | + omit the clearpw column and put '' into the config-query in | |
614 | + its place while doing SELECT on a database. But be ware - | |
615 | + you'll be unable to use authentication methods which needs it, | |
616 | + like MD5_CRAM. | |
14f8443b | 617 | + |
618 | +4.1.2 authdaemon configuration | |
619 | + | |
620 | +When our database is ready we can set up the configuration. :-) Go to | |
621 | +authmysqlrc file and edit it. | |
622 | + | |
623 | +At the beginning we should take care about general informations, which | |
624 | +are identifying our database: | |
625 | + | |
626 | +MYSQL_SERVER localhost | |
627 | +MYSQL_USERNAME vuser | |
628 | +MYSQL_PASSWORD secret_password | |
629 | +MYSQL_DATABASE vmail | |
630 | + | |
631 | +Then we should add a clause responsible for authenticating user and | |
632 | +fetching credentials: | |
633 | + | |
634 | +DEFAULT_DOMAIN exampledom.com | |
635 | + | |
636 | +MYSQL_SELECT_CLAUSE SELECT \ | |
637 | + users.username, users.cryptpw, users.clearpw, \ | |
638 | + domains.uid, domains.gid, \ | |
639 | + CONCAT_WS('/',domains.path_prefix,users.mailbox_path), \ | |
640 | + '', domains.quota, '' \ | |
641 | + FROM users, domains \ | |
642 | + WHERE domains.domain_name='$(domain)' \ | |
643 | + AND users.username='$(local_part)' \ | |
644 | + AND domains.domain_name=users.domain_name | |
645 | + | |
646 | + | |
0e7557ae | 647 | +Note the '' in the place of field which tells where user's INBOX |
648 | +resides and in place of realname field. You should use '' if you want | |
649 | +to put an empty value as a query result for some field. | |
14f8443b | 650 | + |
651 | +We also should add some configuration for changing user's password: | |
652 | + | |
0e7557ae | 653 | +MYSQL_CHPASS_CLAUSE UPDATE \ |
654 | + users \ | |
655 | + SET clearpw='$(newpass)', \ | |
656 | + cryptpw='$(newpass_crypt)' \ | |
657 | + WHERE username='$(local_part)' \ | |
14f8443b | 658 | + AND domain_name='$(domain)' |
659 | + | |
660 | +And finally... | |
661 | +Create a system user/group and a proper directory structure. In our example: | |
662 | + | |
663 | +groupadd -g 555 xdomain | |
664 | +useradd -u 555 -g 555 xdomain | |
665 | +mkdir -p /var/mail/x/s/siefca | |
666 | +chmod -R 0770 /var/mail/x | |
667 | +maildirmake /var/mail/x/s/siefca/Maildir | |
668 | +chown -R xdomain.xdomain /var/mail/x | |
669 | + | |
670 | +Now, restart the authdaemon and see if it works. Try: telnet 0 pop3 | |
671 | + | |
672 | +and type: | |
673 | + | |
674 | +USER siefca [ENTER] | |
675 | +PASS dupa.8 [ENTER] | |
676 | + | |
677 | +You should get Ok response. ;) | |
678 | + | |
679 | +4.2 virtual mail domains provider | |
680 | + | |
0e7557ae | 681 | +Let's consider more complicated database scheme, where there is a need |
682 | +to associate a lot of information with the domain name, including | |
683 | +registrant information, owner, etc. That implies data separation | |
684 | +between domain name, user and domain additional informations (which | |
685 | +are unwanted when authentication process takes place). By proper data | |
686 | +separation I mean avoiding unwanted redundancy in the database. | |
14f8443b | 687 | + |
0e7557ae | 688 | +Currently applied example doesn't care about the update password |
689 | +problem. This is due to current abilities of MySQL and authdaemon | |
690 | +(authmysql). MySQL doesn't support subsequent SELECTs on UPDATE | |
691 | +operation, and authmysql doesn't supports batched queries at the | |
692 | +moment. | |
14f8443b | 693 | + |
694 | +4.2.1 database structure | |
695 | + | |
696 | +table: domain_names | |
697 | + | |
698 | +purpose: associates domain_id with domain name | |
699 | + | |
700 | +fields: domain_name - fully qualified domain name | |
701 | + domain_id - domain identifier | |
702 | + | |
703 | + +----------------+-----------+ | |
704 | + | domain_name | domain_id | | |
705 | + +----------------+-----------+ | |
706 | + | exampledom.com | 1 | | |
707 | + | pld.org.pl | 2 | | |
708 | + | pld.net.pl | 2 | | |
709 | + | foobare.net.uk | 3 | | |
710 | + +----------------+-----------+ | |
711 | + | |
0e7557ae | 712 | +Note, that for pld.org.pl and pld.net.pl the domain identifiers are |
713 | +the same. We can create a domain aliases in such a way. :) | |
14f8443b | 714 | + |
715 | +table: domain_auth | |
716 | + | |
717 | +purpose: associates domain_id with authentication credentials | |
718 | + which are common for all users in the virtual domain | |
719 | + | |
720 | +fields: domain_id - domain identifier | |
721 | + path_prefix - absolute pathname which points to | |
722 | + a directory where domain's mailboxes | |
723 | + are located | |
724 | + quota - default quota for each mailbox | |
725 | + uid - UID used to work on mailboxes | |
726 | + gid - GID used to work on mailboxes | |
727 | + | |
728 | + +------------+---------------+--------+-------+-------+ | |
729 | + | domain_id | path_prefix | quota | uid | gid | | |
730 | + +------------+---------------+--------+-------+-------+ | |
731 | + | 1 | /var/mail/ex | 100000 | 15000 | 15000 | | |
732 | + | 2 | /var/mail/pld | 555500 | 15001 | 15000 | | |
733 | + | 3 | /home/f0/mail | 8000 | 15002 | 15000 | | |
734 | + +------------+---------------+--------+-------+-------+ | |
735 | + | |
736 | +table: domain_info | |
737 | + | |
738 | +purpose: associates domain_id with additional informations | |
739 | + | |
740 | +fields: domain_id - domain identifier | |
741 | + registrant_id - registrant identifier | |
742 | + nic_handle - NIC handle | |
743 | + owner_id - domain's owner identifier | |
744 | + expires - domain's expiration date | |
745 | + | |
746 | + +------------+---------------+------------+----------+---------+ | |
747 | + | domain_id | registrant_id | nic_handle | owner_id | expires | | |
748 | + +------------+---------------+------------+----------+---------+ | |
749 | + | |
750 | + (we don't need to say anything more about this table indeed) | |
751 | + | |
752 | +table: users | |
753 | + | |
754 | +purpose: associates users' identifiers with domains' identifiers | |
755 | + and infers the credentials for various virtual mailboxes | |
756 | + | |
757 | +fields: username - user's login name | |
758 | + domain_id - domain identifier | |
759 | + cryptpw - crypted password | |
760 | + plainpw - plaintext password | |
761 | + quota - user's mailbox quota | |
762 | + (will override quota value set for | |
763 | + the whole virtual domain) | |
764 | + path - relative pathname for mailbox | |
765 | + (will be appended to the path_prefix | |
766 | + from domain_auth table to specify | |
767 | + user's mailbox location) | |
768 | + | |
769 | + +------------+-----------+----------+-----------+-------+------------+ | |
770 | + | username | domain_id | cryptpw | plainpw | quota | path | | |
771 | + +------------+-----------+----------+-----------+-------+------------+ | |
772 | + | foobar | 1 | $1$hlIeE | dupa.8 | NULL | f/o/foobar | | |
773 | + | breeder | 2 | $1$TWsdf | ziarno128 | 77777 | brd | | |
774 | + +------------+-----------+----------+-----------+-------+------------+ | |
775 | + | |
776 | + (you can add a realname column here, it doesn't fit to my terminal window:) | |
777 | + | |
778 | +--------------------- cut here | |
779 | + | |
780 | +# Create the database called vmail. | |
781 | + | |
782 | +CREATE database vmail; | |
783 | + | |
0e7557ae | 784 | +# Create an example MySQL user, which can read, write and delete data |
785 | +# from vmail database. Username: vuser Password: secret_password | |
14f8443b | 786 | + |
787 | +GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.* | |
788 | + TO vuser@localhost | |
789 | + IDENTIFIED BY 'secret_password'; | |
790 | + | |
791 | +FLUSH PRIVILEGES; | |
792 | + | |
793 | +# Create the tables. | |
794 | + | |
795 | +use vmail; | |
796 | + | |
797 | +CREATE TABLE domain_names ( | |
798 | + domain_id int(10) unsigned NOT NULL, | |
799 | + domain_name char(255) DEFAULT '' NOT NULL, | |
800 | + KEY domain_name (domain_name(255)) | |
801 | + ); | |
802 | + | |
803 | +CREATE TABLE domain_auth ( | |
804 | + domain_id int(10) unsigned DEFAULT 1 NOT NULL, | |
805 | + uid int(10) unsigned DEFAULT '15000' NOT NULL, | |
806 | + gid int(10) unsigned DEFAULT '15000' NOT NULL, | |
807 | + path_prefix char(255) DEFAULT '' NOT NULL, | |
808 | + quota char(255) DEFAULT '20000000' NOT NULL, | |
809 | + KEY domain_id (domain_id) | |
810 | + ); | |
811 | + | |
812 | +CREATE TABLE users ( | |
813 | + username char(128) DEFAULT '' NOT NULL, | |
814 | + domain_id int(10) unsigned DEFAULT 1 NOT NULL, | |
815 | + cryptpw char(128) DEFAULT '' NOT NULL, | |
816 | + plainpw char(128) DEFAULT '' NOT NULL, | |
817 | + name char(128) DEFAULT '' NOT NULL, | |
818 | + quota char(255), | |
819 | + path char(255) DEFAULT '' NOT NULL, | |
820 | + KEY username (username(128)) | |
821 | + ); | |
822 | + | |
823 | +# Create an example virtual domain entry | |
824 | +# id : 1 | |
825 | +# name : exampledom.com | |
826 | +# uid : 15000 | |
827 | +# gid : 15000 | |
828 | +# path : /var/mail/example | |
829 | +# quota : 20 Megs per mailbox | |
830 | + | |
831 | +INSERT INTO domain_names VALUES (1, 'exampledom.com'); | |
832 | +INSERT INTO domain_auth VALUES (1, '15000', '15000', '/var/mail/example', | |
833 | + '20000000'); | |
834 | + | |
835 | +# Create an example virtual user entry | |
836 | +# username : siefca | |
837 | +# domain id : 1 (points to exampledom.com) | |
838 | +# cryptpw : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/ | |
839 | +# clearpw : dupa.8 | |
840 | +# name : Pawel Wilk | |
841 | +# quota : NULL (we want it to be fetched from domain_auth table) | |
842 | +# mailbox path : s/i/siefca | |
843 | + | |
844 | +INSERT INTO users VALUES ('siefca', 1, '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/', | |
845 | + 'dupa.8', 'Pawel Wilk', NULL, 's/i/siefca'); | |
846 | + | |
847 | +--------------------- cut here | |
848 | + | |
0e7557ae | 849 | +Ok, we've done what we need. Don't forget to create system user with |
850 | +UID and GID set to 15000, and a directory containing mailboxes (in | |
851 | +this case: /var/mail/example) owned by system user I've mentioned | |
852 | +above. There is also necessary to create Maildir folder structure for | |
853 | +our user inside the virtual domain directory - you can configure your | |
854 | +MTA agent to do such thing when first message arrive or use | |
855 | +maildirmake tool, which comes with Courier-IMAP. | |
14f8443b | 856 | + |
857 | + | |
858 | +4.2.2 authdaemon configuration | |
859 | + | |
860 | +DEFAULT_DOMAIN exampledom.com | |
861 | + | |
862 | +MYSQL_SELECT_CLAUSE SELECT \ | |
863 | + users.username, \ | |
864 | + users.cryptpw, \ | |
865 | + users.plainpw, \ | |
866 | + domain_auth.uid, \ | |
867 | + domain_auth.gid, \ | |
868 | + CONCAT_WS('/',domain_auth.path_prefix,users.path), \ | |
869 | + '', \ | |
870 | + IFNULL(users.quota, domain_auth.quota), \ | |
871 | + users.name \ | |
872 | + FROM users, domain_names, domain_auth \ | |
873 | + WHERE domain_names.domain_name='$(domain)' \ | |
874 | + AND users.username='$(local_part)' \ | |
875 | + AND domain_names.domain_id=users.domain_id \ | |
876 | + AND domain_names.domain_id=domain_auth.domain_id | |
877 | + | |
878 | + | |
879 | +. | |
880 | +. | |
881 | +. | |
882 | +. | |
883 | +. | |
884 | +. | |
885 | + | |
886 | +/////////////////////////// PART II - Developer Notes ///////////////////////// | |
0e7557ae | 887 | + |
14f8443b | 888 | *----------------------- |
889 | 1 Modifications overview | |
890 | *----------------------- | |
891 | ||
892 | -Modified files: authmysqllib.c authmysqlrc | |
893 | +Modified files: authmysqllib.c authmysql.c authmysql.h authmysqlrc | |
894 | ||
895 | Each modified set of instructions is marked by my e-mail address: | |
896 | siefca@pld.org.pl | |
897 | ||
898 | -Changes in the current source code are related to: | |
899 | +Changes in the source code are related to: | |
900 | ||
0e7557ae | 901 | -- sections where the queries are constructed |
902 | +- sections where the queries are constructed [authmysqllib.c] | |
14f8443b | 903 | (including memory allocation for the buffers) |
0e7557ae | 904 | |
905 | when MYSQL_SELECT_CLAUSE or MYSQL_CHPASS_CLAUSE is | |
906 | @@ -95,17 +868,29 @@ | |
907 | passing over current memory allocation and query construction | |
908 | subroutines | |
909 | ||
910 | -- section where the configuration file is read | |
911 | +- section where the configuration file is read [authmysqllib.c] | |
912 | ||
913 | i've had to modify read_env() function to allow line breaks | |
914 | - - now each sequence of the backslash as a first character and | |
915 | + -- now each sequence of the backslash as a first character and | |
14f8443b | 916 | newline as the second is replaced by two whitespaces while |
917 | putting into the buffer | |
918 | ||
0e7557ae | 919 | -- sections where the query is constructed |
920 | + i've also added USER_DOMAIN_SEPARATORS configuration option -- | |
921 | + it is used by get_localpart(), get_domain() and get_username() | |
922 | + functions, which are described below | |
14f8443b | 923 | + |
0e7557ae | 924 | +- sections where the query is constructed [authmysqllib.c] |
14f8443b | 925 | |
926 | selection is made, depending on configuration variables which | |
0e7557ae | 927 | - are set or not - if own query is used |
928 | + are set or not -- if own query is used | |
929 | + | |
930 | +- sections where the user is authenticated against the authinfo [authmysql.c] | |
931 | + | |
932 | + i've detached a part of code responsible for authentication | |
933 | + against crypted and plain password -- now it is in stub | |
934 | + function called auth_mysql_checkpassword() -- due to obtain | |
935 | + more clean code in auth_mysql_login() and | |
936 | + auth_mysql_changepw() around trigger calling functions | |
14f8443b | 937 | |
0e7557ae | 938 | |
939 | ||
940 | @@ -123,14 +908,20 @@ | |
941 | ||
942 | These definitions allows to change substitution marks in an easy way. | |
943 | SV_BEGIN_MARK refers to sequence of characters treated as a prefix of | |
944 | -each substitution variable and SV_END_MARK refers to string which is | |
945 | -a closing suffix. If the expected substitution variable is called | |
946 | +each substitution variable and SV_END_MARK refers to string which is a | |
947 | +closing suffix. If the expected substitution variable is called | |
948 | 'local_part' (without apostrophes) then '$(local_part)' is a valid | |
949 | -string representation for SV_BEGIN_MARK set to "$(" and SV_END_MARK to ")". | |
950 | -MAX_SUBSTITUTION_LEN defines maximal length of a substitution variable's | |
951 | -identifier (name). | |
952 | +string representation for SV_BEGIN_MARK set to "$(" and SV_END_MARK to | |
953 | +")". MAX_SUBSTITUTION_LEN defines maximal length of a substitution | |
954 | +variable's identifier (name). | |
14f8443b | 955 | + |
0e7557ae | 956 | +The last two definitions (SV_BEGIN_LEN and SV_END_LEN) are just for |
957 | +code simplification. | |
14f8443b | 958 | + |
0e7557ae | 959 | +#define DEF_SEPARATORS_SET "@%" |
960 | ||
961 | -The last two definitions are just for code simplification. | |
962 | +The DEF_SEPARATORS_SET directive defines the set of characters, which | |
963 | +are treated as separators when splitting local part from the domain. | |
964 | ||
965 | ||
14f8443b | 966 | |
0e7557ae | 967 | @@ -152,10 +943,10 @@ |
968 | size_t value_length; | |
969 | } ; | |
14f8443b | 970 | |
0e7557ae | 971 | -This structure holds information needed by parsing routines. |
972 | -Using var_data array you may specify a set of string substitutions | |
973 | -which should be done while parsing a query. Last element in array | |
974 | -should have all fields set to zero (null). | |
975 | +This structure holds information needed by parsing routines. Using | |
976 | +var_data array you may specify a set of string substitutions which | |
977 | +should be done while parsing a query. Last element in array should | |
978 | +have all fields set to zero (null). | |
979 | ||
980 | name field - should contain substituted variable name | |
981 | value - should contain string which replaces it | |
982 | @@ -164,9 +955,9 @@ | |
983 | ||
984 | ||
985 | explanation: size is used to increase speed of calculation proccess | |
986 | - value_length is used to cache length of a value during the | |
987 | - parsing subroutines - it helps when substitution variable | |
988 | - occures more than once within the query | |
989 | + value_length is used to cache length of a value during | |
990 | + the parsing subroutines - it helps when substitution | |
991 | + variable occures more than once within the query | |
992 | ||
993 | Example: | |
994 | ||
995 | @@ -177,18 +968,19 @@ | |
996 | }; | |
14f8443b | 997 | |
14f8443b | 998 | In this example we've declared that $(some) in the query should be |
0e7557ae | 999 | -replaced by 'replacement' text, and replacement for $(anotha) will |
1000 | -be defined in the code before passing on the array pointer to | |
14f8443b | 1001 | -the paring function. |
0e7557ae | 1002 | +replaced by 'replacement' text, and replacement for $(anotha) will be |
1003 | +set later in the code, before passing on the array pointer to the | |
1004 | +general parsing function. | |
14f8443b | 1005 | |
1006 | ||
1007 | 3.2 typedef size_t (*parsefunc) | |
0e7557ae | 1008 | |
1009 | typedef int (*parsefunc)(const char *, size_t, void *); | |
1010 | ||
1011 | -This type definition refers to the function pointer, which is used | |
1012 | -to pass plugin functions into the core parsing subroutine. This definition | |
1013 | -is included to simplify the declaration of the parse_core() function. | |
1014 | +This type definition refers to the function pointer, which is used to | |
1015 | +pass plugin functions into the core parsing subroutine. This | |
1016 | +definition is included to simplify the declaration of the parse_core() | |
1017 | +function. | |
1018 | ||
1019 | ||
1020 | ||
1021 | @@ -230,6 +1022,10 @@ | |
14f8443b | 1022 | structure of var_data type, which contains variable definition |
1023 | of a given name. It returns NULL on error or failure. | |
1024 | ||
1025 | +FILES | |
1026 | + | |
1027 | + authlib/authmysqllib.c | |
1028 | + | |
1029 | ||
1030 | 4.2 parse_core | |
1031 | ||
0e7557ae | 1032 | @@ -285,6 +1081,11 @@ |
14f8443b | 1033 | |
1034 | This function returns -1 if an error has occured and 0 if | |
1035 | everything went good. | |
1036 | + | |
1037 | +FILES | |
1038 | + | |
1039 | + authlib/authmysqllib.c | |
1040 | + | |
1041 | ||
1042 | 4.3 ParsePlugin_counter | |
1043 | ||
0e7557ae | 1044 | @@ -314,6 +1115,11 @@ |
14f8443b | 1045 | This function returns the variable size or -1 if an error |
1046 | has occured, 0 if everything went good. | |
1047 | ||
1048 | +FILES | |
1049 | + | |
1050 | + authlib/authmysqllib.c | |
1051 | + | |
1052 | + | |
1053 | 4.4 ParsePlugin_builder | |
1054 | ||
1055 | NAME | |
0e7557ae | 1056 | @@ -333,7 +1139,7 @@ |
14f8443b | 1057 | type pointer and refers to the (char *) pointer variable. |
1058 | After each call it shifts the value of pointer variable (char *) | |
1059 | incrementing it by len bytes. Be careful when using this function | |
1060 | - - its changes the given pointer value. Always operate on an | |
1061 | + - it changes the given pointer value. Always operate on an | |
1062 | additional pointer type variable when passing it as the third | |
1063 | argument. | |
1064 | ||
0e7557ae | 1065 | @@ -342,6 +1148,10 @@ |
14f8443b | 1066 | This function returns the variable size or -1 if an error |
1067 | has occured, 0 if everything went good. | |
1068 | ||
1069 | +FILES | |
1070 | + | |
1071 | + authlib/authmysqllib.c | |
1072 | + | |
1073 | 4.5 parse_string | |
1074 | ||
1075 | NAME | |
0e7557ae | 1076 | @@ -353,7 +1163,7 @@ |
14f8443b | 1077 | |
1078 | DESCRIPTION | |
1079 | ||
1080 | - This function parses the string pointed with source according to the | |
1081 | + This function parses the string pointed to by source according to the | |
1082 | replacement instructions set in var_data array, which is passed with | |
1083 | its pointer vdt. It produces changed string located in newly allocated | |
1084 | memory area. | |
0e7557ae | 1085 | @@ -377,6 +1187,10 @@ |
14f8443b | 1086 | Function returns pointer to the result buffer or NULL |
1087 | if an error has occured. | |
1088 | ||
1089 | +FILES | |
1090 | + | |
1091 | + authlib/authmysqllib.c | |
1092 | + | |
1093 | WARNINGS | |
1094 | ||
1095 | This function allocates some amount of memory using standard | |
0e7557ae | 1096 | @@ -405,6 +1219,10 @@ |
14f8443b | 1097 | It returns a pointer to the static buffer which contains |
1098 | validated password string or NULL if an error has occured. | |
1099 | ||
1100 | +FILES | |
1101 | + | |
1102 | + authlib/authmysqllib.c | |
1103 | + | |
1104 | ||
1105 | 4.7 get_localpart | |
1106 | ||
0e7557ae | 1107 | @@ -414,20 +1232,28 @@ |
14f8443b | 1108 | |
1109 | SYNOPSIS | |
1110 | ||
1111 | - static const char *get_localpart (const char *username); | |
1112 | + static const char *get_localpart (const char *username, | |
1113 | + const char *separators); | |
1114 | ||
1115 | DESCRIPTION | |
1116 | ||
1117 | This function detaches local part of an e-mail address | |
1118 | from string pointed with username and puts it to the | |
1119 | buffer of the fixed length. All necessary cleaning is | |
1120 | - made on the result string. | |
1121 | + made on the result string. String pointed with separators | |
1122 | + refers to a set of characters, which are treated as | |
1123 | + separation signs between local part and a domain. | |
1124 | ||
1125 | RETURN VALUE | |
1126 | ||
1127 | Pointer to the static buffer containing local part or | |
1128 | NULL if there was some error. | |
1129 | ||
1130 | +FILES | |
1131 | + | |
1132 | + authlib/authmysqllib.c | |
1133 | + | |
1134 | + | |
1135 | ||
1136 | 4.8 get_domain | |
1137 | ||
0e7557ae | 1138 | @@ -438,24 +1264,67 @@ |
14f8443b | 1139 | SYNOPSIS |
1140 | ||
1141 | static const char *get_domain (const char *username, | |
1142 | - const char *defdomain); | |
1143 | + const char *defdomain, | |
1144 | + const char *separators); | |
1145 | ||
1146 | DESCRIPTION | |
1147 | ||
1148 | This function detaches domain part of an e-mail address | |
1149 | from string pointed with username and puts it to the | |
1150 | buffer of the fixed length. All necessary cleaning is | |
1151 | - made on the result string. If function cannot find domain | |
1152 | - part in the string the string pointed by defdomain is | |
1153 | - used instead. | |
1154 | + made on the result string. If the function cannot find a domain | |
1155 | + part in the string then the string pointed to by defdomain is | |
1156 | + used instead. If this function cannot find a domain part | |
1157 | + as well as it cannot obtain the default domain (it's empty string | |
1158 | + or the defdomain pointer is NULL) the returned result string is an | |
1159 | + empty string. The string pointed with separators refers to a set | |
1160 | + of characters, which are treated as separation signs between local | |
1161 | + part and a domain. | |
1162 | ||
1163 | RETURN VALUE | |
1164 | ||
1165 | Pointer to the static buffer containing domain name or | |
1166 | NULL if there was some error. | |
1167 | ||
1168 | +FILES | |
1169 | + | |
1170 | + authlib/authmysqllib.c | |
14f8443b | 1171 | |
1172 | -4.9 parse_select_clause | |
0e7557ae | 1173 | + |
14f8443b | 1174 | +4.9 get_username |
1175 | + | |
1176 | +NAME | |
1177 | + | |
1178 | + get_username | |
1179 | + | |
1180 | +SYNOPSIS | |
1181 | + | |
1182 | + static const char *get_username (const char *username, | |
0e7557ae | 1183 | + const char *domainname); |
14f8443b | 1184 | + |
1185 | +DESCRIPTION | |
1186 | + | |
1187 | + This function concatenates the localpart with a domain name | |
0e7557ae | 1188 | + using the @ symbol. If the domain is empty or NULL the result |
1189 | + comes without binding symbol. | |
14f8443b | 1190 | + |
1191 | +RETURN VALUE | |
1192 | + | |
1193 | + Pointer to the static buffer containing output string or | |
1194 | + NULL if there was some error. | |
1195 | + | |
1196 | +FILES | |
1197 | + | |
1198 | + authlib/authmysqllib.c | |
1199 | + | |
1200 | +WARNINGS | |
1201 | + | |
1202 | + This function does not any string cleaning, nor default domain | |
1203 | + checking. It is designed to work on results of get_localpart() and | |
1204 | + get_domain(). | |
1205 | + | |
1206 | + | |
1207 | +4.10 parse_select_clause | |
1208 | ||
1209 | NAME | |
1210 | ||
0e7557ae | 1211 | @@ -465,23 +1334,34 @@ |
14f8443b | 1212 | |
1213 | static char *parse_select_clause (const char *clause, | |
1214 | const char *username, | |
1215 | - const char *defdomain); | |
1216 | + const char *defdomain | |
14f8443b | 1217 | + const char *separators_set); |
1218 | ||
1219 | DESCRIPTION | |
1220 | ||
0e7557ae | 1221 | This function is a simple wrapper to the parse_string() |
14f8443b | 1222 | function. It parses a query pointed by caluse. username |
0e7557ae | 1223 | - and defdomain strings are used to replace corresponding |
1224 | - substitution strings if present in the query: $(local_part) | |
14f8443b | 1225 | - and $(domain). |
0e7557ae | 1226 | + and defdomain strings are used to create corresponding |
1227 | + substitution strings if present in the query: $(local_part), | |
1228 | + $(domain), and $(username). Note, that username parameter | |
1229 | + may contain 'user@domain' form here, so the call to | |
1230 | + get_localpart() and get_domain() function will split it | |
1231 | + into two parts, then calling get_username() function will join | |
1232 | + it again using the @ symbol. This trick is wanted as long as | |
1233 | + we'd like to have possibility to split the local part from the | |
1234 | + domain by using dynamic symbols set. The separators_set is | |
1235 | + passed to get_localpart() and get_domain() invocations. | |
14f8443b | 1236 | |
1237 | - | |
1238 | RETURN VALUE | |
1239 | ||
1240 | Same as parse_string(). | |
1241 | ||
1242 | +FILES | |
1243 | + | |
1244 | + authlib/authmysqllib.c | |
1245 | ||
1246 | -4.10 parse_chpass_clause | |
1247 | + | |
1248 | +4.11 parse_chpass_clause | |
1249 | ||
1250 | NAME | |
1251 | ||
0e7557ae | 1252 | @@ -492,6 +1372,7 @@ |
14f8443b | 1253 | static char *parse_chpass_clause (const char *clause, |
1254 | const char *username, | |
1255 | const char *defdomain, | |
1256 | + const char *separators_set, | |
14f8443b | 1257 | const char *newpass, |
1258 | const char *newpass_crypt); | |
1259 | ||
0e7557ae | 1260 | @@ -502,12 +1383,115 @@ |
14f8443b | 1261 | defdomain, newpass and newpass_crypt strings are used to |
1262 | replace corresponding substitution strings if present in | |
1263 | the query: $(local_part), $(domain), $(newpass), | |
1264 | - $(newpass_crypt). | |
0e7557ae | 1265 | + $(newpass_crypt). The separators_set is passed to |
1266 | + get_localpart() and get_domain() functions as described in the | |
1267 | + entry for parse_select_clause(). | |
14f8443b | 1268 | |
1269 | RETURN VALUE | |
1270 | ||
1271 | Same as parse_string(). | |
1272 | ||
1273 | +FILES | |
1274 | + | |
1275 | + authlib/authmysqllib.c | |
1276 | + | |
1277 | + | |
1278 | +4.12 auth_mysql_on_trigger | |
1279 | + | |
1280 | +NAME | |
1281 | + | |
1282 | + auth_mysql_on_trigger | |
1283 | + | |
1284 | +SYNOPSIS | |
1285 | + | |
1286 | + int auth_mysql_on_trigger (const char *clause_name, | |
1287 | + const char *username); | |
1288 | + | |
1289 | +DESCRIPTION | |
1290 | + | |
0e7557ae | 1291 | + This function is responsible for calling out the MySQL queries |
14f8443b | 1292 | + depending on which authentication state was reached. |
1293 | + | |
0e7557ae | 1294 | + The clause_name should contain the name of a clause, which can |
1295 | + be found in the configuration file, and the username is simply | |
1296 | + the string used as username (including the domain if entered). | |
14f8443b | 1297 | + |
0e7557ae | 1298 | + This function reads DEFAULT_DOMAIN and USER_DOMAIN_SEPARATORS |
1299 | + from the configuration file using read_env(), then it uses | |
1300 | + parse_select_clause() to parse the query obtained using | |
1301 | + read_env(clause_name), and then it calls querying subroutines | |
1302 | + to perform the action. | |
14f8443b | 1303 | + |
1304 | +RETURN VALUE | |
1305 | + | |
0e7557ae | 1306 | + This function returns 0 on success and -1 on failure. The |
1307 | + query results are simply discarded. If a trigger's clause is | |
1308 | + not defined in the configuration file the 1 is returned and | |
1309 | + function silently ends its work. | |
1310 | + | |
1311 | +FILES | |
1312 | + | |
1313 | + authlib/authmysqllib.c | |
1314 | + | |
1315 | + | |
1316 | +4.13 auth_mysql_on_pass | |
1317 | + | |
1318 | +NAME | |
1319 | + | |
1320 | + auth_mysql_on_pass | |
1321 | + | |
1322 | +SYNOPSIS | |
1323 | + | |
1324 | + static int auth_mysql_on_pass(const char *clause, | |
1325 | + struct authmysqluserinfo *authinfo); | |
1326 | + | |
1327 | +DESCRIPTION | |
1328 | + | |
1329 | + This function is responsible for invoking trigger MySQL | |
1330 | + clauses whenever user is authenticated or not. | |
1331 | + This is a stub function, which calls auth_mysql_on_trigger(). | |
1332 | + Firstly, it does a simple checks in authinfo structure -- | |
1333 | + it looks for a valid username field. If username is not set | |
1334 | + or it's empty the fuction does nothing. This behavior follows | |
1335 | + the need, that if there wasn't any valid username then we | |
1336 | + shouldn't touch the database. | |
1337 | + | |
1338 | +RETURN VALUE | |
1339 | + | |
1340 | + It returns 0 in case everything went fine, -1 if there was some | |
1341 | + error. | |
14f8443b | 1342 | + |
1343 | +FILES | |
1344 | + | |
14f8443b | 1345 | + authlib/authmysql.c |
0e7557ae | 1346 | + |
1347 | + | |
1348 | +4.14 auth_mysql_checkpassword | |
1349 | + | |
1350 | +NAME | |
1351 | + auth_mysql_checkpassword | |
1352 | + | |
1353 | +SYNOPSIS | |
1354 | + | |
1355 | + static int auth_mysql_checkpassword(struct authmysqluserinfo *authinfo, const char *pass); | |
1356 | + | |
1357 | +DESCRIPTION | |
1358 | + | |
1359 | + This function is a wrapper, which checks user's entered | |
1360 | + password against one found in a database. Function tries to | |
1361 | + authenticate user against his crypted password and if it's | |
1362 | + impossible it tries the plain form -- by impossible we mean | |
1363 | + the authinfo->cryptpw set to NULL. | |
1364 | + | |
1365 | +RETURN VALUE | |
1366 | + | |
1367 | + Function returns 0 if the password was correct, -1 if user | |
1368 | + applied bad password of the username wasn't found. | |
1369 | + | |
1370 | +FILES | |
1371 | + | |
1372 | + authlib/authmysql.c | |
1373 | + | |
14f8443b | 1374 | |
1375 | ||
1376 | ||
0e7557ae | 1377 | @@ -516,15 +1500,10 @@ |
1378 | 5 Ideas and TODO | |
1379 | *------------------------ | |
1380 | ||
1381 | -- solve problem with fixed buffer length of local part and the domain part | |
1382 | - strings after split (problem?) | |
14f8443b | 1383 | - allow admin to set a group name instead of numerical group id |
1384 | - allow admin to set a username instead of numerical user id | |
1385 | - | |
1386 | -- add clauses: | |
1387 | - | |
1388 | - - MYSQL_PRESELECT_CLAUSE (query which comes before MYSQL_SELECT_CLAUSE) | |
1389 | - - MYSQL_POSTSELECT_CLAUSE (query which comes after MYSQL_SELECT_CLAUSE) | |
14f8443b | 1390 | +- put the parsing routines into separate files to make possible of sharing it |
1391 | + by more authentication modules | |
1392 | ||
1393 | ||
1394 | ||
0e7557ae | 1395 | @@ -534,10 +1513,20 @@ |
14f8443b | 1396 | 6 Thanks |
1397 | *------------------------ | |
1398 | ||
1399 | -At the beginning this patch was messy indeed. :> I would like to thank | |
1400 | -Sam Varshavchik for pointing me a lot how to make it more fast and solid. | |
1401 | -I would also thank Philip Hazel, Chris Lightfoot and Mike Bremford which | |
1402 | -by their software capabilities inspired me to write it. | |
1403 | +At the beginning the patch was messy indeed. :> I would like to thank: | |
0e7557ae | 1404 | |
1405 | ---------------------------------------------------------------------------- | |
14f8443b | 1406 | +Sam Varshavchik |
1407 | + for pointing me a lot, how to make it more fast and solid | |
1408 | + | |
1409 | +Philip Hazel, Chris Lightfoot, Mike Bremford | |
1410 | + which by their software's capabilities inspired me to write it | |
1411 | + | |
1412 | +Oliver Oblasnik | |
1413 | + which remainded me to make the documentation more friendly for | |
1414 | + those who are not programmers and just want to use it | |
1415 | + | |
1416 | +Jacek Surazski | |
1417 | + for reviewing this document just before it was published | |
1418 | ||
0e7557ae | 1419 | +--------------------------------------------------------------------------- |
1420 | + Any comments and suggestions are welcome. | |
14f8443b | 1421 | diff -ur courier-imap-1.5.3-orig/authlib/authmysql.c courier-imap-1.5.3/authlib/authmysql.c |
1422 | --- courier-imap-1.5.3-orig/authlib/authmysql.c Sun Jun 24 01:42:05 2001 | |
0e7557ae | 1423 | +++ courier-imap-1.5.3/authlib/authmysql.c Sun Oct 13 23:12:06 2002 |
1424 | @@ -19,7 +19,47 @@ | |
1425 | #include "authmysql.h" | |
1426 | #include "authstaticlist.h" | |
1427 | ||
1428 | -static const char rcsid[]="$Id$"; | |
1429 | +static const char rcsid[]="$Id$"; | |
1430 | + | |
1431 | +/* siefca@pld.org.pl */ | |
1432 | +static int auth_mysql_on_pass(const char *clause, struct authmysqluserinfo *authinfo) | |
1433 | +{ | |
1434 | + if (authinfo->username && *(authinfo->username)!='\0') /* do it if user was found */ | |
1435 | + { | |
1436 | + if (auth_mysql_on_trigger(clause, authinfo->username)) | |
1437 | + { | |
1438 | + return (-1); /* MySQL error or something critical.. */ | |
1439 | + } | |
1440 | + } | |
1441 | + | |
1442 | + return (0); | |
1443 | +} | |
1444 | + | |
1445 | +/* siefca@pld.org.pl */ | |
1446 | +static int auth_mysql_checkpassword(struct authmysqluserinfo *authinfo, | |
1447 | + const char *pass) | |
1448 | +{ | |
1449 | + if (authinfo->cryptpw) | |
1450 | + { | |
1451 | + if (authcheckpassword(pass,authinfo->cryptpw)) | |
1452 | + { | |
1453 | + return (-1); /* User/Password not found. */ | |
1454 | + } | |
1455 | + } | |
1456 | + else if (authinfo->clearpw) | |
1457 | + { | |
1458 | + if (strcmp(pass, authinfo->clearpw)) | |
1459 | + { | |
1460 | + return (-1); | |
1461 | + } | |
1462 | + } | |
1463 | + else | |
1464 | + { | |
1465 | + return (-1); | |
1466 | + } | |
1467 | + | |
1468 | + return (0); | |
1469 | +} | |
1470 | ||
1471 | static char *auth_mysql_login(const char *service, char *authdata, | |
1472 | int issession, | |
1473 | @@ -46,26 +86,23 @@ | |
14f8443b | 1474 | return (0); |
1475 | } | |
1476 | ||
0e7557ae | 1477 | - if (authinfo->cryptpw) |
1478 | + /* siefca@pld.org.pl */ | |
1479 | + if (auth_mysql_checkpassword(authinfo,pass)) | |
14f8443b | 1480 | { |
0e7557ae | 1481 | - if (authcheckpassword(pass,authinfo->cryptpw)) |
1482 | - { | |
1483 | + if (auth_mysql_on_pass("ON_PASS_FAIL_CLAUSE", authinfo)) | |
1484 | + errno=EACCES; | |
1485 | + else | |
1486 | errno=EPERM; | |
1487 | - return (0); /* User/Password not found. */ | |
1488 | - } | |
1489 | - } | |
1490 | - else if (authinfo->clearpw) | |
1491 | - { | |
1492 | - if (strcmp(pass, authinfo->clearpw)) | |
1493 | - { | |
14f8443b | 1494 | - errno=EPERM; |
0e7557ae | 1495 | - return (0); |
1496 | - } | |
1497 | + | |
1498 | + return(0); | |
14f8443b | 1499 | } |
1500 | else | |
1501 | { | |
1502 | - errno=EPERM; | |
0e7557ae | 1503 | - return (0); /* Username not found */ |
1504 | + if (auth_mysql_on_pass("ON_PASS_OK_CLAUSE", authinfo)) | |
1505 | + { | |
1506 | + errno=EACCES; | |
1507 | + return(0); | |
1508 | + } | |
14f8443b | 1509 | } |
1510 | ||
0e7557ae | 1511 | if (callback_func == 0) |
1512 | @@ -149,26 +186,23 @@ | |
1513 | return (-1); | |
14f8443b | 1514 | } |
1515 | ||
0e7557ae | 1516 | - if (authinfo->cryptpw) |
1517 | + /* siefca@pld.org.pl */ | |
1518 | + if (auth_mysql_checkpassword(authinfo, pass)) | |
14f8443b | 1519 | { |
0e7557ae | 1520 | - if (authcheckpassword(pass,authinfo->cryptpw)) |
1521 | - { | |
14f8443b | 1522 | - errno=EPERM; |
0e7557ae | 1523 | - return (-1); /* User/Password not found. */ |
1524 | - } | |
1525 | - } | |
1526 | - else if (authinfo->clearpw) | |
1527 | - { | |
1528 | - if (strcmp(pass, authinfo->clearpw)) | |
1529 | - { | |
1530 | + if (auth_mysql_on_pass("ON_PASS_FAIL_CLAUSE", authinfo)) | |
1531 | + errno=EACCES; | |
1532 | + else | |
1533 | errno=EPERM; | |
1534 | - return (-1); | |
1535 | - } | |
14f8443b | 1536 | + |
0e7557ae | 1537 | + return(-1); |
14f8443b | 1538 | } |
1539 | else | |
1540 | { | |
1541 | - errno=EPERM; | |
0e7557ae | 1542 | - return (-1); |
1543 | + if (auth_mysql_on_pass("ON_PASS_OK_CLAUSE", authinfo)) | |
1544 | + { | |
14f8443b | 1545 | + errno=EACCES; |
0e7557ae | 1546 | + return(-1); |
1547 | + } | |
14f8443b | 1548 | } |
1549 | ||
0e7557ae | 1550 | if (auth_mysql_setpass(user, newpass)) |
1551 | @@ -176,6 +210,14 @@ | |
14f8443b | 1552 | errno=EPERM; |
1553 | return (-1); | |
1554 | } | |
1555 | + | |
0e7557ae | 1556 | + /* siefca@pld.org.pl */ |
1557 | + if (auth_mysql_on_pass("ON_PASS_CHANGE_CLAUSE", authinfo)) | |
14f8443b | 1558 | + { |
0e7557ae | 1559 | + errno=EACCES; |
1560 | + return (-1); | |
14f8443b | 1561 | + } |
0e7557ae | 1562 | + |
14f8443b | 1563 | return (0); |
1564 | } | |
1565 | ||
0e7557ae | 1566 | @@ -314,7 +356,7 @@ |
1567 | #endif | |
1568 | ||
1569 | char *auth_mysql(const char *service, const char *authtype, char *authdata, | |
1570 | - int issession, | |
1571 | + int issession, | |
1572 | void (*callback_func)(struct authinfo *, void *), void *callback_arg) | |
1573 | { | |
1574 | if (strcmp(authtype, AUTHTYPE_LOGIN) == 0) | |
14f8443b | 1575 | diff -ur courier-imap-1.5.3-orig/authlib/authmysql.h courier-imap-1.5.3/authlib/authmysql.h |
1576 | --- courier-imap-1.5.3-orig/authlib/authmysql.h Mon Aug 6 05:12:39 2001 | |
0e7557ae | 1577 | +++ courier-imap-1.5.3/authlib/authmysql.h Sat Sep 28 00:01:07 2002 |
14f8443b | 1578 | @@ -21,6 +21,7 @@ |
1579 | } ; | |
1580 | ||
1581 | extern struct authmysqluserinfo *auth_mysql_getuserinfo(const char *); | |
1582 | +extern int auth_mysql_on_trigger (const char *clause_name, const char *username); | |
1583 | extern void auth_mysql_cleanup(); | |
1584 | ||
1585 | extern int auth_mysql_setpass(const char *, const char *); | |
1586 | diff -ur courier-imap-1.5.3-orig/authlib/authmysqllib.c courier-imap-1.5.3/authlib/authmysqllib.c | |
1587 | --- courier-imap-1.5.3-orig/authlib/authmysqllib.c Wed May 29 19:24:03 2002 | |
0e7557ae | 1588 | +++ courier-imap-1.5.3/authlib/authmysqllib.c Sun Oct 13 22:58:09 2002 |
1589 | @@ -23,6 +23,8 @@ | |
14f8443b | 1590 | #define SV_END_MARK ")" |
1591 | #define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1) | |
1592 | #define SV_END_LEN ((sizeof(SV_END_MARK))-1) | |
14f8443b | 1593 | +#define DEF_SEPARATORS_SET "@%" |
1594 | + | |
1595 | ||
1596 | static const char rcsid[]="$Id$"; | |
1597 | ||
0e7557ae | 1598 | @@ -268,7 +270,7 @@ |
14f8443b | 1599 | SV_BEGIN_MARK |
1600 | "%.*s" | |
1601 | SV_END_MARK | |
1602 | - "\n", len, begin); | |
1603 | + "\n", (int) len, begin); | |
1604 | ||
1605 | return NULL; | |
1606 | } | |
0e7557ae | 1607 | @@ -364,14 +366,14 @@ |
1608 | t_size = t_end-t_begin+1;/* text field length */ | |
1609 | ||
1610 | /* work on text */ | |
1611 | - if ( (outfn (t_begin, t_size, result)) == -1 ) | |
1612 | + if ( (outfn (t_begin, t_size, result))) | |
1613 | return -1; | |
1614 | ||
1615 | /* work on variable */ | |
1616 | v_ptr = get_variable (v_begin, v_size, vdt); | |
1617 | if (!v_ptr) return -1; | |
1618 | ||
1619 | - if ( (outfn (v_ptr->value, v_ptr->value_length, result)) == -1 ) | |
1620 | + if ( (outfn (v_ptr->value, v_ptr->value_length, result))) | |
1621 | return -1; | |
1622 | ||
1623 | q = e + 1; | |
1624 | @@ -379,7 +381,7 @@ | |
1625 | ||
1626 | /* work on last part of text if any */ | |
1627 | if (*q != '\0') | |
1628 | - if ( (outfn (q, strlen(q), result)) == -1 ) | |
1629 | + if ( (outfn (q, strlen(q), result))) | |
1630 | return -1; | |
1631 | ||
1632 | return 0; | |
1633 | @@ -426,21 +428,45 @@ | |
14f8443b | 1634 | return NULL; |
1635 | } | |
1636 | *pass_buf = '\0'; | |
1637 | - | |
1638 | + | |
1639 | return output_buf; | |
1640 | } | |
1641 | ||
1642 | /* siefca@pld.org.pl */ | |
1643 | -static const char *get_localpart (const char *username) | |
0e7557ae | 1644 | +static const char *get_username (const char *username, const char *domainname) |
14f8443b | 1645 | +{ |
0e7557ae | 1646 | +size_t u_len; |
1647 | +char *p; | |
14f8443b | 1648 | +static char username_buf[400]; |
1649 | + | |
0e7557ae | 1650 | + if (!username || *username == '\0') return NULL; |
1651 | + u_len=strlen(username); | |
1652 | + if (( u_len + (domainname ? strlen(domainname) : 0) | |
1653 | + ) > 397) return NULL; | |
1654 | + | |
1655 | + strcpy (username_buf, username); | |
1656 | + if (domainname && *domainname != '\0') | |
1657 | + { | |
1658 | + p = username_buf + u_len; | |
1659 | + *p='@'; p++; | |
1660 | + strcpy(p, domainname); | |
1661 | + } | |
1662 | + | |
14f8443b | 1663 | + return (username_buf); |
1664 | +} | |
1665 | + | |
1666 | +/* siefca@pld.org.pl */ | |
1667 | +static const char *get_localpart (const char *username, const char *separators) | |
1668 | { | |
1669 | size_t lbuf = 0; | |
1670 | const char *l_end, *p; | |
1671 | char *q; | |
1672 | static char localpart_buf[130]; | |
1673 | ||
1674 | - if (!username || *username == '\0') return NULL; | |
1675 | + if (!username || *username == '\0' || | |
1676 | + !separators || *separators == '\0') return NULL; | |
1677 | ||
1678 | - p = strchr(username,'@'); | |
1679 | + p = strpbrk (username, separators); | |
1680 | if (p) | |
1681 | { | |
1682 | if ((p-username) > 128) | |
0e7557ae | 1683 | @@ -469,21 +495,27 @@ |
14f8443b | 1684 | } |
1685 | ||
1686 | /* siefca@pld.org.pl */ | |
1687 | -static const char *get_domain (const char *username, const char *defdomain) | |
1688 | +static const char *get_domain (const char *username, const char *defdomain, | |
1689 | + const char *separators) | |
1690 | { | |
1691 | static char domain_buf[260]; | |
1692 | const char *p; | |
1693 | char *q; | |
1694 | ||
1695 | - if (!username || *username == '\0') return NULL; | |
1696 | - p = strchr(username,'@'); | |
1697 | + if (!username || *username == '\0' || | |
1698 | + !separators || *separators == '\0') return NULL; | |
1699 | + | |
1700 | + p = strpbrk (username, separators); | |
1701 | ||
1702 | if (!p || *(p+1) == '\0') | |
1703 | { | |
1704 | - if (defdomain && *defdomain) | |
1705 | + if (defdomain && *defdomain != '\0') | |
1706 | return defdomain; | |
1707 | else | |
1708 | - return NULL; | |
1709 | + { | |
1710 | + *domain_buf = '\0'; | |
1711 | + return domain_buf; | |
1712 | + } | |
1713 | } | |
1714 | ||
1715 | p++; | |
0e7557ae | 1716 | @@ -531,20 +563,25 @@ |
14f8443b | 1717 | |
1718 | /* siefca@pld.org.pl */ | |
1719 | static char *parse_select_clause (const char *clause, const char *username, | |
1720 | - const char *defdomain) | |
1721 | + const char *defdomain, | |
14f8443b | 1722 | + const char *separators_set) |
1723 | { | |
1724 | static struct var_data vd[]={ | |
1725 | {"local_part", NULL, sizeof("local_part"), 0}, | |
1726 | {"domain", NULL, sizeof("domain"), 0}, | |
1727 | + {"username", NULL, sizeof("username"), 0}, | |
1728 | {NULL, NULL, 0, 0}}; | |
1729 | ||
0e7557ae | 1730 | - if (clause == NULL || *clause == '\0' || |
14f8443b | 1731 | - !username || *username == '\0') |
0e7557ae | 1732 | + if (!clause || !username || !separators_set || |
1733 | + *clause == '\0' || *username == '\0' || | |
1734 | + *separators_set == '\0') | |
14f8443b | 1735 | return NULL; |
0e7557ae | 1736 | - |
14f8443b | 1737 | - vd[0].value = get_localpart (username); |
1738 | - vd[1].value = get_domain (username, defdomain); | |
1739 | - if (!vd[0].value || !vd[1].value) | |
0e7557ae | 1740 | + |
14f8443b | 1741 | + vd[0].value = get_localpart (username, separators_set); |
1742 | + vd[1].value = get_domain (username, defdomain, separators_set); | |
0e7557ae | 1743 | + vd[2].value = get_username (vd[0].value, vd[1].value); |
14f8443b | 1744 | + |
1745 | + if (!vd[0].value || !vd[1].value || !vd[2].value) | |
1746 | return NULL; | |
1747 | ||
1748 | return (parse_string (clause, vd)); | |
0e7557ae | 1749 | @@ -552,12 +589,15 @@ |
14f8443b | 1750 | |
1751 | /* siefca@pld.org.pl */ | |
1752 | static char *parse_chpass_clause (const char *clause, const char *username, | |
1753 | - const char *defdomain, const char *newpass, | |
1754 | + const char *defdomain, | |
1755 | + const char *separators_set, | |
14f8443b | 1756 | + const char *newpass, |
1757 | const char *newpass_crypt) | |
1758 | { | |
1759 | static struct var_data vd[]={ | |
1760 | {"local_part", NULL, sizeof("local_part"), 0}, | |
1761 | {"domain", NULL, sizeof("domain"), 0}, | |
1762 | + {"username", NULL, sizeof("username"), 0}, | |
1763 | {"newpass", NULL, sizeof("newpass"), 0}, | |
1764 | {"newpass_crypt", NULL, sizeof("newpass_crypt"), 0}, | |
1765 | {NULL, NULL, 0, 0}}; | |
0e7557ae | 1766 | @@ -565,19 +605,81 @@ |
14f8443b | 1767 | if (clause == NULL || *clause == '\0' || |
1768 | !username || *username == '\0' || | |
1769 | !newpass || *newpass == '\0' || | |
1770 | + !separators_set || *separators_set == '\0' || | |
1771 | !newpass_crypt || *newpass_crypt == '\0') return NULL; | |
1772 | ||
1773 | - vd[0].value = get_localpart (username); | |
1774 | - vd[1].value = get_domain (username, defdomain); | |
1775 | - vd[2].value = validate_password (newpass); | |
1776 | - vd[3].value = validate_password (newpass_crypt); | |
1777 | + vd[0].value = get_localpart (username, separators_set); | |
1778 | + vd[1].value = get_domain (username, defdomain, separators_set); | |
0e7557ae | 1779 | + vd[3].value = get_username (vd[0].value, vd[1].value); |
14f8443b | 1780 | + vd[4].value = validate_password (newpass); |
1781 | + vd[5].value = validate_password (newpass_crypt); | |
1782 | ||
1783 | if (!vd[0].value || !vd[1].value || | |
1784 | - !vd[2].value || !vd[3].value) return NULL; | |
1785 | + !vd[2].value || !vd[3].value || | |
1786 | + !vd[4].value || !vd[5].value) return NULL; | |
1787 | ||
1788 | return (parse_string (clause, vd)); | |
1789 | } | |
1790 | ||
1791 | +/* siefca@pld.org.pl */ | |
1792 | +int auth_mysql_on_trigger (const char *clause_name, const char *username) | |
1793 | +{ | |
1794 | +char *querybuf =NULL; | |
0e7557ae | 1795 | +const char *separators_set =NULL, |
14f8443b | 1796 | + *defdomain =NULL, |
1797 | + *on_clause =NULL; | |
1798 | +MYSQL_RES *result; | |
1799 | + | |
0e7557ae | 1800 | + if (!clause_name || *clause_name == '\0') |
1801 | + return (-1); | |
1802 | + | |
14f8443b | 1803 | + on_clause = read_env (clause_name); |
0e7557ae | 1804 | + if (!on_clause || *on_clause == '\0') |
1805 | + return (0); /* ok! not in use */ | |
14f8443b | 1806 | + |
1807 | + defdomain = read_env ("DEFAULT_DOMAIN"); | |
14f8443b | 1808 | + separators_set = read_env ("USER_DOMAIN_SEPARATORS"); |
1809 | + if (!defdomain) defdomain = ""; | |
14f8443b | 1810 | + if (!separators_set || *separators_set == '\0') |
1811 | + separators_set = DEF_SEPARATORS_SET; | |
1812 | + | |
1813 | + querybuf = parse_select_clause (on_clause, | |
1814 | + username, | |
1815 | + defdomain, | |
14f8443b | 1816 | + separators_set); |
1817 | + | |
0e7557ae | 1818 | + if (!querybuf) return (-1); |
14f8443b | 1819 | + |
1820 | + if (mysql_query (mysql, querybuf)) | |
1821 | + { | |
1822 | + /* <o.blasnik@nextra.de> */ | |
1823 | + | |
1824 | + auth_mysql_cleanup(); | |
1825 | + | |
1826 | + if (do_connect()) | |
1827 | + { | |
1828 | + free(querybuf); | |
0e7557ae | 1829 | + return (-1); |
14f8443b | 1830 | + } |
1831 | + | |
1832 | + if (mysql_query (mysql, querybuf)) | |
1833 | + { | |
1834 | + free(querybuf); | |
1835 | + auth_mysql_cleanup(); | |
1836 | + /* Server went down, that's OK, | |
1837 | + ** try again next time. | |
1838 | + */ | |
0e7557ae | 1839 | + return (-1); |
14f8443b | 1840 | + } |
1841 | + } | |
1842 | + free(querybuf); | |
1843 | + result = mysql_store_result(mysql); | |
1844 | + if (result) mysql_free_result(result); | |
1845 | + | |
0e7557ae | 1846 | + return (0); |
14f8443b | 1847 | +} |
1848 | + | |
1849 | + | |
1850 | struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username) | |
1851 | { | |
1852 | const char *user_table =NULL; | |
0e7557ae | 1853 | @@ -596,6 +698,7 @@ |
14f8443b | 1854 | *gid_field =NULL, |
1855 | *quota_field =NULL, | |
1856 | *where_clause =NULL, | |
14f8443b | 1857 | + *separators_set =NULL, |
1858 | *select_clause =NULL; /* siefca@pld.org.pl */ | |
1859 | ||
1860 | static const char query[]= | |
0e7557ae | 1861 | @@ -704,7 +807,15 @@ |
14f8443b | 1862 | else |
1863 | { | |
1864 | /* siefca@pld.org.pl */ | |
1865 | - querybuf=parse_select_clause (select_clause, username, defdomain); | |
14f8443b | 1866 | + separators_set = read_env ("USER_DOMAIN_SEPARATORS"); |
1867 | + | |
14f8443b | 1868 | + if (!separators_set || *separators_set == '\0') |
1869 | + separators_set = DEF_SEPARATORS_SET; | |
1870 | + | |
1871 | + querybuf = parse_select_clause (select_clause, | |
1872 | + username, | |
1873 | + defdomain, | |
14f8443b | 1874 | + separators_set); |
1875 | if (!querybuf) return 0; | |
1876 | } | |
1877 | ||
0e7557ae | 1878 | @@ -788,6 +899,7 @@ |
14f8443b | 1879 | *where_clause =NULL, |
1880 | *user_table =NULL, | |
1881 | *login_field =NULL, | |
14f8443b | 1882 | + *separators_set =NULL, |
1883 | *chpass_clause =NULL; /* siefca@pld.org.pl */ | |
1884 | ||
1885 | if (!mysql) | |
0e7557ae | 1886 | @@ -837,13 +949,18 @@ |
14f8443b | 1887 | } |
1888 | else | |
1889 | { | |
14f8443b | 1890 | + separators_set = read_env ("USER_DOMAIN_SEPARATORS"); |
1891 | + | |
14f8443b | 1892 | + if (!separators_set || *separators_set == '\0') |
1893 | + separators_set = DEF_SEPARATORS_SET; | |
1894 | + | |
1895 | sql_buf=parse_chpass_clause(chpass_clause, | |
1896 | user, | |
1897 | defdomain, | |
14f8443b | 1898 | + separators_set, |
1899 | pass, | |
1900 | newpass_crypt_ptr); | |
1901 | } | |
1902 | - | |
1903 | ||
1904 | if (!sql_buf) | |
1905 | { | |
14f8443b | 1906 | diff -ur courier-imap-1.5.3-orig/authlib/authmysqlrc courier-imap-1.5.3/authlib/authmysqlrc |
1907 | --- courier-imap-1.5.3-orig/authlib/authmysqlrc Thu Apr 4 06:36:29 2002 | |
0e7557ae | 1908 | +++ courier-imap-1.5.3/authlib/authmysqlrc Mon Oct 14 00:02:59 2002 |
14f8443b | 1909 | @@ -1,4 +1,4 @@ |
1910 | -##VERSION: $Id$ | |
1911 | +##VERSION: $Id$ | |
1912 | # | |
1913 | # Copyright 2000 Double Precision, Inc. See COPYING for | |
1914 | # distribution information. | |
1915 | @@ -141,65 +141,99 @@ | |
1916 | # | |
1917 | # MYSQL_WHERE_CLAUSE server='mailhost.example.com' | |
1918 | ||
1919 | -##NAME: MYSQL_SELECT_CLAUSE:0 | |
1920 | -# | |
1921 | -# (EXPERIMENTAL) | |
1922 | -# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database, | |
1923 | -# which is structuraly different from proposed. The fixed string will | |
1924 | -# be used to do a SELECT operation on database, which should return fields | |
1925 | -# in order specified bellow: | |
14f8443b | 1926 | +##NAME: USER_DOMAIN_SEPARATORS:0 |
1927 | # | |
0e7557ae | 1928 | -# username, cryptpw, uid, gid, clearpw, home, maildir, quota, fullname |
14f8443b | 1929 | +# This is optional. Using this option you can set the set of characters |
1930 | +# which are treated as separators when splitting entered username into the | |
1931 | +# local part and the domain name. If it's not set the defaults @% are used, | |
1932 | +# so the user can authenticate using user@domain or user%domain form. | |
1933 | +# See README.authmysql.myownquery for more information | |
1934 | # | |
0e7557ae | 1935 | -# Enabling this option causes ignorance of any other field-related |
1936 | -# options, excluding default domain. | |
14f8443b | 1937 | +# USER_DOMAIN_SEPARATORS @%+ |
1938 | + | |
1939 | +##NAME: MYSQL_SELECT_CLAUSE:0 | |
1940 | # | |
0e7557ae | 1941 | -# There are two variables, which you can use. Substitution will be made |
1942 | -# for them, so you can put entered username (local part) and domain name | |
1943 | -# in the right place of your query. These variables are: | |
1944 | -# $(local_part) and $(domain) | |
14f8443b | 1945 | +# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database, |
1946 | +# which is structuraly different from proposed. You can type here your MySQL | |
1947 | +# query, which will be used to fetch user's credentials, and which should | |
1948 | +# return fields in order specified bellow: | |
1949 | +# | |
1950 | +# username, cryptpw, clearpw, uid, gid, home, maildir, quota, fullname | |
1951 | +# | |
1952 | +# Enabling this option causes ignorance of any other field-related options. | |
1953 | +# | |
1954 | +# There also are variables, which you can use. Substitution will be made | |
1955 | +# for them, so you can pass currently entered username and a domain name | |
1956 | +# up to the right place within your query. These variables are: | |
1957 | +# $(local_part) , $(domain) , $(username) | |
1958 | # | |
0e7557ae | 1959 | # If a $(domain) is empty (not given by the remote user) the default domain |
1960 | -# name is used in its place. | |
1961 | -# | |
1962 | -# This example is a little bit modified adaptation of vmail-sql | |
1963 | -# database scheme: | |
1964 | -# | |
1965 | -# MYSQL_SELECT_CLAUSE SELECT popbox.local_part, \ | |
1966 | -# CONCAT('{MD5}', popbox.password_hash), \ | |
1967 | -# popbox.clearpw, \ | |
1968 | -# domain.uid, \ | |
1969 | -# domain.gid, \ | |
1970 | -# CONCAT(domain.path, '/', popbox.mbox_name), \ | |
1971 | -# '', \ | |
1972 | -# domain.quota, \ | |
1973 | -# '', \ | |
1974 | -# FROM popbox, domain \ | |
1975 | -# WHERE popbox.local_part = '$(local_part)' \ | |
1976 | -# AND popbox.domain_name = '$(domain)' \ | |
1977 | -# AND popbox.domain_name = domain.domain_name | |
1978 | -# | |
14f8443b | 1979 | +# name is used in its place. $(username) is a local part concatenated with |
1980 | +# domain name using symbol defined in USER_DOMAIN_CONCAT or '@' if this option | |
1981 | +# is not set. | |
1982 | +# See README.authmysql.myownquery for more information | |
1983 | +# | |
1984 | +# MYSQL_SELECT_CLAUSE SELECT \ | |
1985 | +# users.username, users.cryptpw, users.clearpw, \ | |
1986 | +# domains.uid, domains.gid, \ | |
1987 | +# CONCAT_WS('/',domains.path_prefix,users.mailbox_path), \ | |
1988 | +# '', domains.quota, '' \ | |
1989 | +# FROM users, domains \ | |
1990 | +# WHERE domains.domain_name='$(domain)' \ | |
1991 | +# AND users.username='$(local_part)' \ | |
1992 | +# AND domains.domain_name=users.domain_name | |
1993 | + | |
1994 | ##NAME: MYSQL_CHPASS_CLAUSE:0 | |
1995 | # | |
1996 | -# (EXPERIMENTAL) | |
1997 | # This is optional, MYSQL_CHPASS_CLAUSE can be set when you have a database, | |
1998 | -# which is structuraly different from proposed. The fixed string will | |
1999 | -# be used to do an UPDATE operation on database. In other words, it is | |
2000 | -# used, when changing password. | |
2001 | +# which is structuraly different from proposed. You can use it to set up | |
2002 | +# a MySQL query used to change user's password. | |
2003 | # | |
2004 | # There are four variables, which you can use. Substitution will be made | |
2005 | -# for them, so you can put entered username (local part) and domain name | |
2006 | -# in the right place of your query. There variables are: | |
2007 | -# $(local_part) , $(domain) , $(newpass) , $(newpass_crypt) | |
2008 | +# for them, so you can put the currently entered username and the domain name | |
2009 | +# in the right place of your query. These variables are: | |
2010 | +# $(local_part) , $(domain) , $(username) , $(newpass) , $(newpass_crypt) | |
2011 | # | |
2012 | # If a $(domain) is empty (not given by the remote user) the default domain | |
2013 | -# name is used in its place. | |
2014 | -# $(newpass) contains plain password | |
2015 | -# $(newpass_crypt) contains its crypted form | |
2016 | -# | |
2017 | -# MYSQL_CHPASS_CLAUSE UPDATE popbox \ | |
2018 | -# SET clearpw='$(newpass)', \ | |
2019 | -# password_hash='$(newpass_crypt)' \ | |
2020 | -# WHERE local_part='$(local_part)' \ | |
2021 | -# AND domain_name='$(domain)' | |
0e7557ae | 2022 | -# |
14f8443b | 2023 | +# name is used in its place. $(newpass) contains plain password and |
2024 | +# $(newpass_crypt) contains its crypted form. | |
2025 | +# See README.authmysql.myownquery for more information | |
2026 | +# | |
2027 | +# MYSQL_CHPASS_CLAUSE UPDATE users \ | |
2028 | +# SET clearpw='$(newpass)', \ | |
2029 | +# cryptpw='$(newpass_crypt)' \ | |
2030 | +# WHERE username='$(local_part)' \ | |
2031 | +# AND domain_name='$(domain)' | |
2032 | + | |
0e7557ae | 2033 | +##NAME: ON_PASS_OK_CLAUSE:0 |
14f8443b | 2034 | +# |
0e7557ae | 2035 | +# This is optional, ON_PASS_OK_CLAUSE is a trigger -- the query |
2036 | +# is performed each time user has successfuly logged in. | |
14f8443b | 2037 | +# See README.authmysql.myownquery for more information |
2038 | +# | |
0e7557ae | 2039 | +# ON_PASS_OK_CLAUSE UPDATE users \ |
14f8443b | 2040 | +# SET last_ok=CURRENT_TIMESTAMP \ |
2041 | +# WHERE username='$(local_part)' \ | |
2042 | +# AND domain_name='$(domain)' | |
2043 | + | |
0e7557ae | 2044 | +##NAME: ON_PASS_FAIL_CLAUSE:0 |
2045 | +# | |
2046 | +# This is optional, ON_PASS_FAIL_CLAUSE is a trigger -- the query | |
2047 | +# is performed each time user has NOT logged in, cause of bad password. | |
14f8443b | 2048 | +# See README.authmysql.myownquery for more information |
2049 | +# | |
0e7557ae | 2050 | +# ON_PASS_FAIL_CLAUSE UPDATE users \ |
14f8443b | 2051 | +# SET last_fail=CURRENT_TIMESTAMP \ |
2052 | +# WHERE username='$(local_part)' \ | |
2053 | +# AND domain_name='$(domain)' | |
0e7557ae | 2054 | + |
2055 | +##NAME: ON_PASS_CHANGE_CLAUSE:0 | |
2056 | +# | |
2057 | +# This is optional, ON_PASS_CHANGE_CLAUSE is a trigger -- the query | |
2058 | +# is performed each time user has successfuly changed his password. | |
2059 | +# See README.authmysql.myownquery for more information | |
2060 | +# | |
2061 | +# ON_PASS_CHANGE_CLAUSE UPDATE users \ | |
2062 | +# SET pw_change=CURRENT_TIMESTAMP \ | |
2063 | +# WHERE username='$(local_part)' \ | |
2064 | +# AND domain_name='$(domain)' |