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