]> git.pld-linux.org Git - packages/courier-imap.git/blame - courier-imap-1.5.3-myownquery.patch
- security note (no fixes known, 3.0.0 has this part of code mostly rewritten)
[packages/courier-imap.git] / courier-imap-1.5.3-myownquery.patch
CommitLineData
14f8443b 1diff -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 1421diff -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 1575diff -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 *);
1586diff -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 1906diff -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)'
This page took 0.550451 seconds and 4 git commands to generate.