a_amv_lopts_add(): with HAVE_PUTENV, do not save ENV pointer (memleak)
[s-mailx.git] / obs-imap-gssapi.h
blob17da3deb14c89cc5cf61bbce3b6f85e2cc1d47ed
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Implementation of IMAP GSS-API authentication according to RFC 1731.
3 *@ TODO GSS-API should also be joined into "a VFS".
5 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
6 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
7 */
8 /*
9 * Copyright (c) 2004 Gunnar Ritter.
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by Gunnar Ritter
23 * and his contributors.
24 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
42 * Partially derived from sample code in:
44 * GSS-API Programming Guide
45 * Part No: 816-1331-11
46 * Sun Microsystems, Inc. 4150 Network Circle Santa Clara, CA 95054 U.S.A.
48 * (c) 2002 Sun Microsystems
51 * Copyright 1994 by OpenVision Technologies, Inc.
53 * Permission to use, copy, modify, distribute, and sell this software
54 * and its documentation for any purpose is hereby granted without fee,
55 * provided that the above copyright notice appears in all copies and
56 * that both that copyright notice and this permission notice appear in
57 * supporting documentation, and that the name of OpenVision not be used
58 * in advertising or publicity pertaining to distribution of the software
59 * without specific, written prior permission. OpenVision makes no
60 * representations about the suitability of this software for any
61 * purpose. It is provided "as is" without express or implied warranty.
63 * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
64 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
65 * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
66 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
67 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
68 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
69 * PERFORMANCE OF THIS SOFTWARE.
72 #ifdef HAVE_GSSAPI
73 #ifndef GSSAPI_REG_INCLUDE
74 # include <gssapi/gssapi.h>
75 # ifdef GSSAPI_OLD_STYLE
76 # include <gssapi/gssapi_generic.h>
77 # define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
78 # define NAIL_DEFINED_GCC_C_NT_HOSTBASED_SERVICE
79 # endif
80 #else
81 # include <gssapi.h>
82 #endif
84 static void _imap_gssapi_error1(const char *s, OM_uint32 code, int typ);
85 static void _imap_gssapi_error(const char *s, OM_uint32 maj_stat,
86 OM_uint32 min_stat);
87 static char * _imap_gssapi_last_at_before_slash(char const *sp);
89 static void
90 _imap_gssapi_error1(const char *s, OM_uint32 code, int typ)
92 OM_uint32 maj_stat, min_stat;
93 gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
94 OM_uint32 msg_ctx = 0;
95 NYD_ENTER;
97 do {
98 maj_stat = gss_display_status(&min_stat, code, typ, GSS_C_NO_OID,
99 &msg_ctx, &msg);
100 if (maj_stat == GSS_S_COMPLETE) {
101 fprintf(stderr, "GSS error: %s / %.*s\n",
102 s, (int)msg.length, (char*)msg.value);
103 gss_release_buffer(&min_stat, &msg);
104 } else {
105 fprintf(stderr, "GSS error: %s / unknown\n", s);
106 break;
108 } while (msg_ctx);
109 NYD_LEAVE;
112 static void
113 _imap_gssapi_error(const char *s, OM_uint32 maj_stat, OM_uint32 min_stat)
115 NYD_ENTER;
116 _imap_gssapi_error1(s, maj_stat, GSS_C_GSS_CODE);
117 _imap_gssapi_error1(s, min_stat, GSS_C_MECH_CODE);
118 NYD_LEAVE;
121 static char *
122 _imap_gssapi_last_at_before_slash(char const *sp)
124 char const *cp;
125 char c;
126 NYD_ENTER;
128 for (cp = sp; (c = *cp) != '\0'; ++cp)
129 if (c == '/')
130 break;
131 while (cp > sp && *--cp != '@')
133 if (*cp != '@')
134 cp = NULL;
135 NYD_LEAVE;
136 return n_UNCONST(cp);
139 static enum okay
140 _imap_gssapi(struct mailbox *mp, struct ccred *ccred)
142 char o[LINESIZE];
143 struct str in, out;
144 gss_buffer_desc send_tok, recv_tok;
145 gss_name_t target_name;
146 gss_ctx_id_t gss_context;
147 OM_uint32 maj_stat, min_stat, ret_flags;
148 int conf_state;
149 FILE *queuefp = NULL;
150 char *server, *cp;
151 enum{
152 a_F_NONE,
153 a_F_RECV_TOK = 1u<<0,
154 a_F_SEND_TOK = 1u<<1,
155 a_F_TARGET_NAME = 1u<<2,
156 a_F_GSS_CONTEXT = 1u<<3
157 } f;
158 enum okay ok;
159 NYD_X;
161 ok = STOP;
162 f = a_F_NONE;
164 { size_t i = strlen(mp->mb_imap_account) +1;
165 server = salloc(i);
166 memcpy(server, mp->mb_imap_account, i);
168 if (!strncmp(server, "imap://", 7))
169 server += 7;
170 else if (!strncmp(server, "imaps://", 8))
171 server += 8;
172 if ((cp = _imap_gssapi_last_at_before_slash(server)) != NULL)
173 server = &cp[1];
174 for (cp = server; *cp; cp++)
175 *cp = lowerconv(*cp);
176 send_tok.value = n_autorec_alloc(
177 (send_tok.length = strlen(server) -1 + 5) +1);
178 snprintf(send_tok.value, send_tok.length, "imap@%s", server);
179 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NT_HOSTBASED_SERVICE,
180 &target_name);
181 f |= a_F_TARGET_NAME;
182 if (maj_stat != GSS_S_COMPLETE) {
183 _imap_gssapi_error(savestrbuf(send_tok.value, send_tok.length),
184 maj_stat, min_stat);
185 goto jleave;
188 gss_context = GSS_C_NO_CONTEXT;
189 maj_stat = gss_init_sec_context(&min_stat,
190 GSS_C_NO_CREDENTIAL,
191 &gss_context,
192 target_name,
193 GSS_C_NO_OID,
194 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
196 GSS_C_NO_CHANNEL_BINDINGS,
197 GSS_C_NO_BUFFER,
198 NULL,
199 &send_tok,
200 &ret_flags,
201 NULL);
202 f |= a_F_SEND_TOK | a_F_GSS_CONTEXT;
203 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
204 _imap_gssapi_error("initializing GSS context", maj_stat, min_stat);
205 goto jleave;
208 snprintf(o, sizeof o, "%s AUTHENTICATE GSSAPI\r\n", tag(1));
209 IMAP_OUT(o, 0, goto jleave);
212 * No response data expected.
214 imap_answer(mp, 1);
215 if (response_type != RESPONSE_CONT)
216 goto jleave;
217 while (maj_stat == GSS_S_CONTINUE_NEEDED) {
218 /* Pass token obtained from first gss_init_sec_context() call. */
219 if(b64_encode_buf(&out, send_tok.value, send_tok.length,
220 B64_SALLOC | B64_CRLF) == NULL)
221 goto jleave;
222 gss_release_buffer(&min_stat, &send_tok);
223 f &= ~a_F_SEND_TOK;
224 IMAP_OUT(out.s, 0, goto jleave);
225 imap_answer(mp, 1);
226 if (response_type != RESPONSE_CONT)
227 goto jleave;
228 out.s = NULL;
229 in.s = responded_text;
230 in.l = strlen(responded_text);
231 if(!b64_decode(&out, &in))
232 goto jebase64;
233 recv_tok.value = out.s;
234 recv_tok.length = out.l;
235 maj_stat = gss_init_sec_context(&min_stat,
236 GSS_C_NO_CREDENTIAL,
237 &gss_context,
238 target_name,
239 GSS_C_NO_OID,
240 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
242 GSS_C_NO_CHANNEL_BINDINGS,
243 &recv_tok,
244 NULL,
245 &send_tok,
246 &ret_flags,
247 NULL);
248 free(out.s);
249 f |= a_F_SEND_TOK;
250 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
251 _imap_gssapi_error("initializing context", maj_stat, min_stat);
252 goto jleave;
256 gss_release_name(&min_stat, &target_name);
257 f &= ~a_F_TARGET_NAME;
259 /* Pass token obtained from second gss_init_sec_context() call */
260 if(b64_encode_buf(&out, send_tok.value, send_tok.length,
261 B64_SALLOC | B64_CRLF) == NULL)
262 goto jleave;
263 IMAP_OUT(out.s, 0, goto jleave);
265 gss_release_buffer(&min_stat, &send_tok);
266 f &= ~a_F_SEND_TOK;
269 * First octet: bit-mask with protection mechanisms.
270 * Second to fourth octet: maximum message size in network byte order.
272 * This code currently does not care about the values.
274 imap_answer(mp, 1);
275 if (response_type != RESPONSE_CONT)
276 goto jleave;
277 out.s = NULL;
278 in.s = responded_text;
279 in.l = strlen(responded_text);
280 if(!b64_decode(&out, &in)){
281 jebase64:
282 if(out.s != NULL)
283 free(out.s);
284 n_err(_("Invalid base64 encoding from GSSAPI server\n"));
285 goto jleave;
287 recv_tok.value = out.s;
288 recv_tok.length = out.l;
289 maj_stat = gss_unwrap(&min_stat, gss_context, &recv_tok, &send_tok,
290 &conf_state, NULL);
291 free(out.s);
292 gss_release_buffer(&min_stat, &send_tok);
293 /*f &= ~a_F_SEND_TOK;*/
294 if (maj_stat != GSS_S_COMPLETE) {
295 _imap_gssapi_error("unwrapping data", maj_stat, min_stat);
296 goto jleave;
299 /* First octet: bit-mask with protection mechanisms (1 = no protection
300 * mechanism).
301 * Second to fourth octet: maximum message size in network byte order.
302 * Fifth and following octets: user name string.
304 o[0] = 1;
305 o[1] = 0;
306 o[2] = o[3] = (char)0377;
307 snprintf(&o[4], sizeof o - 4, "%s", ccred->cc_user.s);
308 send_tok.value = o;
309 send_tok.length = strlen(&o[4]) -1 + 4;
310 maj_stat = gss_wrap(&min_stat, gss_context, 0, GSS_C_QOP_DEFAULT, &send_tok,
311 &conf_state, &recv_tok);
312 f |= a_F_RECV_TOK;
313 if (maj_stat != GSS_S_COMPLETE) {
314 _imap_gssapi_error("wrapping data", maj_stat, min_stat);
315 goto jleave;
318 if(b64_encode_buf(&out, recv_tok.value, recv_tok.length,
319 B64_SALLOC | B64_CRLF) == NULL)
320 goto jleave;
321 IMAP_OUT(out.s, MB_COMD, goto jleave);
323 while (mp->mb_active & MB_COMD)
324 ok = imap_answer(mp, 1);
325 jleave:
326 if(f & a_F_RECV_TOK)
327 gss_release_buffer(&min_stat, &recv_tok);
328 if(f & a_F_SEND_TOK)
329 gss_release_buffer(&min_stat, &send_tok);
330 if(f & a_F_TARGET_NAME)
331 gss_release_name(&min_stat, &target_name);
332 if(f & a_F_GSS_CONTEXT)
333 gss_delete_sec_context(&min_stat, &gss_context, GSS_C_NO_BUFFER);
334 return ok;
337 # ifdef NAIL_DEFINED_GCC_C_NT_HOSTBASED_SERVICE
338 # undef GSS_C_NT_HOSTBASED_SERVICE
339 # endif
340 #endif /* HAVE_GSSAPI */
342 /* s-it-mode */