3c13b9fb |
1 | <?php |
2 | |
35586184 |
3 | /** |
4 | * auth.php |
5 | * |
82d304a0 |
6 | * Copyright (c) 1999-2004 The SquirrelMail Project Team |
35586184 |
7 | * Licensed under the GNU GPL. For full terms see the file COPYING. |
8 | * |
9 | * Contains functions used to do authentication. |
10 | * |
11 | * $Id$ |
d6c32258 |
12 | * @package squirrelmail |
35586184 |
13 | */ |
14 | |
d6c32258 |
15 | /** Put in a safety net here, in case a naughty admin didn't run conf.pl when they upgraded */ |
47a29326 |
16 | |
17 | if (! isset($smtp_auth_mech)) { |
18 | $smtp_auth_mech = 'none'; |
19 | } |
20 | |
21 | if (! isset($imap_auth_mech)) { |
fe0b18b3 |
22 | $imap_auth_mech = 'login'; |
47a29326 |
23 | } |
24 | |
25 | if (! isset($use_imap_tls)) { |
26 | $use_imap_tls = false; |
27 | } |
28 | |
29 | if (! isset($use_smtp_tls)) { |
30 | $use_smtp_tls = false; |
31 | } |
32 | |
8b52a4ca |
33 | /** |
34 | * Check if user has previously logged in to the Squirrelmail session. If user |
35 | * has not logged in, execution will stop inside this function. |
36 | * |
37 | * @return int A positive value is returned if user has previously logged in |
38 | * successfully. |
39 | */ |
9be8198d |
40 | function is_logged_in() { |
2d367c68 |
41 | |
d7c82551 |
42 | if ( sqsession_is_registered('user_is_logged_in') ) { |
e110c214 |
43 | return; |
4d2c9f70 |
44 | } else { |
8a48b083 |
45 | global $PHP_SELF, $session_expired_post, |
f04cab07 |
46 | $session_expired_location; |
47 | |
48 | /* First we store some information in the new session to prevent |
49 | * information-loss. |
50 | */ |
8a48b083 |
51 | |
52 | $session_expired_post = $_POST; |
f04cab07 |
53 | $session_expired_location = $PHP_SELF; |
d7c82551 |
54 | if (!sqsession_is_registered('session_expired_post')) { |
8a48b083 |
55 | sqsession_register($session_expired_post,'session_expired_post'); |
f04cab07 |
56 | } |
d7c82551 |
57 | if (!sqsession_is_registered('session_expired_location')) { |
8a48b083 |
58 | sqsession_register($session_expired_location,'session_expired_location'); |
f04cab07 |
59 | } |
014bba80 |
60 | include_once( SM_PATH . 'functions/display_messages.php' ); |
9be8198d |
61 | logout_error( _("You must be logged in to access this page.") ); |
4d2c9f70 |
62 | exit; |
e110c214 |
63 | } |
e110c214 |
64 | } |
3c13b9fb |
65 | |
8b52a4ca |
66 | /** |
67 | * Given the challenge from the server, supply the response using cram-md5 (See |
68 | * RFC 2195 for details) |
69 | * |
70 | * @param string $username User ID |
71 | * @param string $password User password supplied by User |
72 | * @param string $challenge The challenge supplied by the server |
73 | * @return string The response to be sent to the IMAP server |
74 | * |
75 | */ |
47a29326 |
76 | function cram_md5_response ($username,$password,$challenge) { |
8b52a4ca |
77 | $challenge=base64_decode($challenge); |
78 | $hash=bin2hex(hmac_md5($challenge,$password)); |
79 | $response=base64_encode($username . " " . $hash) . "\r\n"; |
80 | return $response; |
47a29326 |
81 | } |
82 | |
8b52a4ca |
83 | /** |
84 | * Return Digest-MD5 response. |
85 | * Given the challenge from the server, calculate and return the |
86 | * response-string for digest-md5 authentication. (See RFC 2831 for more |
87 | * details) |
88 | * |
89 | * @param string $username User ID |
90 | * @param string $password User password supplied by User |
91 | * @param string $challenge The challenge supplied by the server |
92 | * @param string $service The service name, usually 'imap'; it is used to |
93 | * define the digest-uri. |
94 | * @param string $host The host name, usually the server's FQDN; it is used to |
95 | * define the digest-uri. |
96 | * @return string The response to be sent to the IMAP server |
97 | */ |
47a29326 |
98 | function digest_md5_response ($username,$password,$challenge,$service,$host) { |
47a29326 |
99 | $result=digest_md5_parse_challenge($challenge); |
100 | |
101 | // verify server supports qop=auth |
83c400e7 |
102 | // $qop = explode(",",$result['qop']); |
47a29326 |
103 | //if (!in_array("auth",$qop)) { |
104 | // rfc2831: client MUST fail if no qop methods supported |
105 | // return false; |
106 | //} |
1c6d997a |
107 | $cnonce = base64_encode(bin2hex(hmac_md5(microtime()))); |
47a29326 |
108 | $ncount = "00000001"; |
109 | |
110 | /* This can be auth (authentication only), auth-int (integrity protection), or |
111 | auth-conf (confidentiality protection). Right now only auth is supported. |
112 | DO NOT CHANGE THIS VALUE */ |
113 | $qop_value = "auth"; |
114 | |
115 | $digest_uri_value = $service . '/' . $host; |
116 | |
117 | // build the $response_value |
118 | //FIXME This will probably break badly if a server sends more than one realm |
119 | $string_a1 = utf8_encode($username).":"; |
120 | $string_a1 .= utf8_encode($result['realm']).":"; |
121 | $string_a1 .= utf8_encode($password); |
1c6d997a |
122 | $string_a1 = hmac_md5($string_a1); |
47a29326 |
123 | $A1 = $string_a1 . ":" . $result['nonce'] . ":" . $cnonce; |
1c6d997a |
124 | $A1 = bin2hex(hmac_md5($A1)); |
47a29326 |
125 | $A2 = "AUTHENTICATE:$digest_uri_value"; |
126 | // If qop is auth-int or auth-conf, A2 gets a little extra |
127 | if ($qop_value != 'auth') { |
128 | $A2 .= ':00000000000000000000000000000000'; |
129 | } |
1c6d997a |
130 | $A2 = bin2hex(hmac_md5($A2)); |
47a29326 |
131 | |
132 | $string_response = $result['nonce'] . ':' . $ncount . ':' . $cnonce . ':' . $qop_value; |
1c6d997a |
133 | $response_value = bin2hex(hmac_md5($A1.":".$string_response.":".$A2)); |
47a29326 |
134 | |
135 | $reply = 'charset=utf-8,username="' . $username . '",realm="' . $result["realm"] . '",'; |
136 | $reply .= 'nonce="' . $result['nonce'] . '",nc=' . $ncount . ',cnonce="' . $cnonce . '",'; |
137 | $reply .= "digest-uri=\"$digest_uri_value\",response=$response_value"; |
138 | $reply .= ',qop=' . $qop_value; |
139 | $reply = base64_encode($reply); |
140 | return $reply . "\r\n"; |
141 | |
142 | } |
143 | |
8b52a4ca |
144 | /** |
145 | * Parse Digest-MD5 challenge. |
146 | * This function parses the challenge sent during DIGEST-MD5 authentication and |
147 | * returns an array. See the RFC for details on what's in the challenge string. |
148 | * |
149 | * @param string $challenge Digest-MD5 Challenge |
150 | * @return array Digest-MD5 challenge decoded data |
151 | */ |
47a29326 |
152 | function digest_md5_parse_challenge($challenge) { |
47a29326 |
153 | $challenge=base64_decode($challenge); |
83c400e7 |
154 | while (isset($challenge)) { |
47a29326 |
155 | if ($challenge{0} == ',') { // First char is a comma, must not be 1st time through loop |
156 | $challenge=substr($challenge,1); |
157 | } |
158 | $key=explode('=',$challenge,2); |
159 | $challenge=$key[1]; |
160 | $key=$key[0]; |
161 | if ($challenge{0} == '"') { |
162 | // We're in a quoted value |
163 | // Drop the first quote, since we don't care about it |
164 | $challenge=substr($challenge,1); |
165 | // Now explode() to the next quote, which is the end of our value |
166 | $val=explode('"',$challenge,2); |
167 | $challenge=$val[1]; // The rest of the challenge, work on it in next iteration of loop |
168 | $value=explode(',',$val[0]); |
169 | // Now, for those quoted values that are only 1 piece.. |
170 | if (sizeof($value) == 1) { |
171 | $value=$value[0]; // Convert to non-array |
172 | } |
173 | } else { |
174 | // We're in a "simple" value - explode to next comma |
175 | $val=explode(',',$challenge,2); |
83c400e7 |
176 | if (isset($val[1])) { |
177 | $challenge=$val[1]; |
178 | } else { |
179 | unset($challenge); |
180 | } |
47a29326 |
181 | $value=$val[0]; |
182 | } |
183 | $parsed["$key"]=$value; |
184 | } // End of while loop |
185 | return $parsed; |
186 | } |
187 | |
8b52a4ca |
188 | /** |
189 | * Creates a HMAC digest that can be used for auth purposes |
190 | * See RFCs 2104, 2617, 2831 |
191 | * Uses mhash() extension if available |
192 | * |
193 | * @param string $data Data to apply hash function to. |
194 | * @param string $key Optional key, which, if supplied, will be used to |
195 | * calculate data's HMAC. |
196 | * @return string HMAC Digest string |
197 | */ |
1c6d997a |
198 | function hmac_md5($data, $key='') { |
639c7164 |
199 | if (extension_loaded('mhash')) { |
200 | if ($key== '') { |
201 | $mhash=mhash(MHASH_MD5,$data); |
202 | } else { |
203 | $mhash=mhash(MHASH_MD5,$data,$key); |
204 | } |
205 | return $mhash; |
206 | } |
207 | if (!$key) { |
208 | return pack('H*',md5($data)); |
209 | } |
210 | $key = str_pad($key,64,chr(0x00)); |
211 | if (strlen($key) > 64) { |
212 | $key = pack("H*",md5($key)); |
213 | } |
214 | $k_ipad = $key ^ str_repeat(chr(0x36), 64) ; |
215 | $k_opad = $key ^ str_repeat(chr(0x5c), 64) ; |
1c6d997a |
216 | /* Heh, let's get recursive. */ |
217 | $hmac=hmac_md5($k_opad . pack("H*",md5($k_ipad . $data)) ); |
639c7164 |
218 | return $hmac; |
219 | } |
220 | |
9bd3b1e6 |
221 | /** |
222 | * Fillin user and password based on SMTP auth settings. |
223 | * |
9bd3b1e6 |
224 | * @param string $user Reference to SMTP username |
225 | * @param string $pass Reference to SMTP password (unencrypted) |
226 | */ |
227 | function get_smtp_user(&$user, &$pass) { |
228 | global $username, $smtp_auth_mech, |
229 | $smtp_sitewide_user, $smtp_sitewide_pass; |
230 | |
231 | if ($smtp_auth_mech == 'none') { |
232 | $user = ''; |
233 | $pass = ''; |
234 | } elseif ( isset($smtp_sitewide_user) && isset($smtp_sitewide_pass) ) { |
235 | $user = $smtp_sitewide_user; |
236 | $pass = $smtp_sitewide_pass; |
237 | } else { |
238 | global $key, $onetimepad; |
239 | $user = $username; |
240 | $pass = OneTimePadDecrypt($key, $onetimepad); |
241 | } |
242 | } |
243 | |
d7c82551 |
244 | ?> |