Commit 66ad7271 66ad72714b35295c9cbaaa199ff7bb37f015c011 by Sergey Poznyakoff

Added to the repository

1 parent c98fcdb8
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
18 #include "imap4d.h"
19 #include <gsasl.h>
20 #include <mailutils/gsasl.h>
21
22 static Gsasl_ctx *ctx;
23 static Gsasl_session_ctx *sess_ctx;
24
25 static int
26 create_gsasl_stream (stream_t *newstr, stream_t str, int flags)
27 {
28 int rc;
29
30 rc = gsasl_stream_create (newstr, str, sess_ctx, flags);
31 if (rc)
32 {
33 syslog (LOG_ERR, _("cannot create SASL stream: %s"),
34 mu_errstring (rc));
35 return RESP_NO;
36 }
37
38 if ((rc = stream_open (*newstr)) != 0)
39 {
40 char *p;
41 if (stream_strerror (*newstr, &p))
42 p = mu_errstring (rc);
43 syslog (LOG_ERR, _("cannot open SASL input stream: %s"), p);
44 return RESP_NO;
45 }
46
47 return RESP_OK;
48 }
49
50
51 static int
52 auth_gsasl (struct imap4d_command *command,
53 char *auth_type, char *arg, char **username)
54 {
55 char *input = NULL;
56 char output[512];
57 size_t output_len;
58 char *s, *client_init;
59 int rc;
60
61 client_init = util_getword (arg, &s);
62 if (!client_init)
63 return RESP_BAD;
64 util_unquote (&client_init);
65
66 rc = gsasl_server_start (ctx, auth_type, &sess_ctx);
67 if (rc != GSASL_OK)
68 {
69 syslog (LOG_NOTICE, _("SASL gsasl_server_start: %s"),
70 gsasl_strerror(rc));
71 return 0;
72 }
73
74 gsasl_server_application_data_set (sess_ctx, username);
75
76 input = client_init;
77 output[0] = '\0';
78 output_len = sizeof (output);
79
80 while ((rc = gsasl_server_step_base64 (sess_ctx, input, output, output_len))
81 == GSASL_NEEDS_MORE)
82 {
83 util_send ("+ %s\r\n", output);
84 input = imap4d_readline_ex ();
85 }
86 while (rc == GSASL_NEEDS_MORE);
87
88 if (rc != GSASL_OK)
89 {
90 syslog (LOG_NOTICE, _("GSASL error: %s"), gsasl_strerror (rc));
91 return RESP_NO;
92 }
93
94 if (*username == NULL)
95 {
96 syslog (LOG_NOTICE, _("GSASL %s: cannot get username"), auth_type);
97 return RESP_NO;
98 }
99
100 if (sess_ctx)
101 {
102 stream_t in, out, new_in, new_out;
103
104 util_get_input (&in);
105 util_get_output (&out);
106 if (create_gsasl_stream (&new_in, in, MU_STREAM_READ))
107 return RESP_NO;
108 if (create_gsasl_stream (&new_out, out, MU_STREAM_WRITE))
109 {
110 stream_destroy (&new_in, stream_get_owner (new_in));
111 return RESP_NO;
112 }
113
114 util_set_input (new_in);
115 util_set_output (new_out);
116 }
117
118 auth_gsasl_capa_init (1);
119 return RESP_OK;
120 }
121
122 static void
123 auth_gsasl_capa_init (int disable)
124 {
125 int rc;
126 char *listmech, *name, *s;
127 size_t size;
128
129 rc = gsasl_server_listmech (ctx, NULL, &size);
130 if (rc != GSASL_OK)
131 return;
132
133 listmech = malloc (size);
134 if (!listmech)
135 imap4d_bye (ERR_NO_MEM);
136
137 rc = gsasl_server_listmech (ctx, listmech, &size);
138 if (rc != GSASL_OK)
139 return;
140
141 for (name = strtok_r (listmech, " ", &s); name;
142 name = strtok_r (NULL, " ", &s))
143 {
144 if (disable)
145 auth_remove (name);
146 else
147 auth_add (strdup (name), auth_gsasl);
148 }
149
150 free (listmech);
151 }
152
153 /* This is for DIGEST-MD5 */
154 static int
155 cb_realm (Gsasl_session_ctx *ctx, char *out, size_t *outlen, size_t nth)
156 {
157 char *realm = util_localname ();
158
159 if (nth > 0)
160 return GSASL_NO_MORE_REALMS;
161
162 if (out)
163 {
164 if (*outlen < strlen (realm))
165 return GSASL_TOO_SMALL_BUFFER;
166 memcpy (out, realm, strlen (realm));
167 }
168
169 *outlen = strlen (realm);
170
171 return GSASL_OK;
172 }
173
174 static int
175 cb_validate (Gsasl_session_ctx *ctx,
176 const char *authorization_id,
177 const char *authentication_id,
178 const char *password)
179 {
180 char **username = gsasl_server_application_data_get (ctx);
181 *username = strdup (authentication_id ?
182 authentication_id : authorization_id);
183 return GSASL_OK;
184 }
185
186 #define GSSAPI_SERVICE "imap"
187
188 static int
189 cb_service (Gsasl_session_ctx *ctx, char *srv, size_t *srvlen,
190 char *host, size_t *hostlen)
191 {
192 char *hostname = util_localname ();
193
194 if (srv)
195 {
196 if (*srvlen < strlen (GSSAPI_SERVICE))
197 return GSASL_TOO_SMALL_BUFFER;
198
199 memcpy (srv, GSSAPI_SERVICE, strlen (GSSAPI_SERVICE));
200 }
201
202 if (srvlen)
203 *srvlen = strlen (GSSAPI_SERVICE);
204
205 if (host)
206 {
207 if (*hostlen < strlen (hostname))
208 return GSASL_TOO_SMALL_BUFFER;
209
210 memcpy (host, hostname, strlen (hostname));
211 }
212
213 if (hostlen)
214 *hostlen = strlen (hostname);
215
216 return GSASL_OK;
217 }
218
219 /* This gets called when SASL mechanism EXTERNAL is invoked */
220 static int
221 cb_external (Gsasl_session_ctx *ctx)
222 {
223 return GSASL_AUTHENTICATION_ERROR;
224 }
225
226 void
227 auth_gsasl_init ()
228 {
229 int rc;
230
231 rc = gsasl_init (&ctx);
232 if (rc != GSASL_OK)
233 {
234 syslog (LOG_NOTICE, _("cannot initialize libgsasl: %s"),
235 gsasl_strerror (rc));
236 }
237
238 gsasl_server_callback_realm_set (ctx, cb_realm);
239 gsasl_server_callback_external_set (ctx, cb_external);
240 gsasl_server_callback_validate_set (ctx, cb_validate);
241 gsasl_server_callback_service_set (ctx, cb_service);
242
243 auth_gsasl_capa_init (0);
244 }
245