make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / smtp-gssapi.h
blob8243d99a7f81145e3f409f4e2ac3ffddbed7043e
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Implementation of SMTP GSS-API authentication according to RFC 4954.
3 *@ TODO GSS-API should also be joined into "a VFS".
5 * Copyright (c) 2014 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-4-Clause
7 */
9 /* Derived from `imap_gssapi.h', which is:
11 * Copyright (c) 2004 Gunnar Ritter.
12 * All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
16 * are met:
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by Gunnar Ritter
25 * and his contributors.
26 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * 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 m_DEFINED_GCC_C_NT_HOSTBASED_SERVICE
80 # endif
81 #else
82 # include <gssapi.h>
83 #endif
85 static void _smtp_gssapi_error1(char const *s, OM_uint32 code, int typ);
86 static void _smtp_gssapi_error(char const *s, OM_uint32 maj_stat,
87 OM_uint32 min_stat);
89 static void
90 _smtp_gssapi_error1(char const *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 n_err(_("GSS error: %s / %.*s\n"),
102 s, (int)msg.length, (char*)msg.value);
103 gss_release_buffer(&min_stat, &msg);
104 } else {
105 n_err(_("GSS error: %s / unknown\n"), s);
106 break;
108 } while (msg_ctx);
109 NYD_LEAVE;
112 static void
113 _smtp_gssapi_error(char const *s, OM_uint32 maj_stat, OM_uint32 min_stat)
115 NYD_ENTER;
116 _smtp_gssapi_error1(s, maj_stat, GSS_C_GSS_CODE);
117 _smtp_gssapi_error1(s, min_stat, GSS_C_MECH_CODE);
118 NYD_LEAVE;
121 static bool_t
122 _smtp_gssapi(struct sock *sp, struct sendbundle *sbp, struct smtp_line *slp)
124 struct str in, out;
125 gss_buffer_desc send_tok, recv_tok;
126 gss_name_t target_name;
127 gss_ctx_id_t gss_context;
128 OM_uint32 maj_stat, min_stat, ret_flags;
129 int conf_state;
130 enum{
131 a_F_NONE,
132 a_F_RECV_TOK = 1u<<0,
133 a_F_SEND_TOK = 1u<<1,
134 a_F_TARGET_NAME = 1u<<2,
135 a_F_GSS_CONTEXT = 1u<<3
136 } f;
137 bool_t ok;
138 NYD_ENTER;
140 ok = FAL0;
141 f = a_F_NONE;
143 if(INT_MAX - 1 - 4 <= sbp->sb_ccred.cc_user.l)
144 goto jleave;
146 send_tok.value = n_autorec_alloc(
147 (send_tok.length = sbp->sb_url.url_host.l + 5) +1);
148 memcpy(send_tok.value, "smtp@", 5);
149 memcpy((char*)send_tok.value + 5, sbp->sb_url.url_host.s,
150 sbp->sb_url.url_host.l +1);
151 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NT_HOSTBASED_SERVICE,
152 &target_name);
153 f |= a_F_TARGET_NAME;
154 if (maj_stat != GSS_S_COMPLETE) {
155 _smtp_gssapi_error(savestrbuf(send_tok.value, send_tok.length),
156 maj_stat, min_stat);
157 goto jleave;
160 gss_context = GSS_C_NO_CONTEXT;
161 maj_stat = gss_init_sec_context(&min_stat,
162 GSS_C_NO_CREDENTIAL,
163 &gss_context,
164 target_name,
165 GSS_C_NO_OID,
166 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
168 GSS_C_NO_CHANNEL_BINDINGS,
169 GSS_C_NO_BUFFER,
170 NULL,
171 &send_tok,
172 &ret_flags,
173 NULL);
174 f |= a_F_SEND_TOK | a_F_GSS_CONTEXT;
175 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
176 _smtp_gssapi_error("initializing GSS context", maj_stat, min_stat);
177 goto jleave;
180 _OUT(NETLINE("AUTH GSSAPI"));
181 _ANSWER(3, FAL0, FAL0);
182 while (maj_stat == GSS_S_CONTINUE_NEEDED) {
183 /* Pass token obtained from first gss_init_sec_context() call */
184 if(b64_encode_buf(&out, send_tok.value, send_tok.length,
185 B64_SALLOC | B64_CRLF) == NULL)
186 goto jleave;
187 gss_release_buffer(&min_stat, &send_tok);
188 f &= ~a_F_SEND_TOK;
189 _OUT(out.s);
190 _ANSWER(3, FAL0, TRU1);
192 out.s = NULL;
193 in.s = slp->dat;
194 in.l = slp->datlen;
195 if(!b64_decode(&out, &in))
196 goto jebase64;
197 recv_tok.value = out.s;
198 recv_tok.length = out.l;
199 maj_stat = gss_init_sec_context(&min_stat,
200 GSS_C_NO_CREDENTIAL,
201 &gss_context,
202 target_name,
203 GSS_C_NO_OID,
204 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
206 GSS_C_NO_CHANNEL_BINDINGS,
207 &recv_tok,
208 NULL,
209 &send_tok,
210 &ret_flags,
211 NULL);
212 n_free(out.s);
213 f |= a_F_SEND_TOK;
214 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
215 _smtp_gssapi_error("initializing context", maj_stat, min_stat);
216 goto jleave;
220 gss_release_name(&min_stat, &target_name);
221 f &= ~a_F_TARGET_NAME;
223 /* Pass token obtained from second gss_init_sec_context() call */
224 if(b64_encode_buf(&out, send_tok.value, send_tok.length,
225 B64_SALLOC | B64_CRLF) == NULL)
226 goto jleave;
227 _OUT(out.s);
229 gss_release_buffer(&min_stat, &send_tok);
230 f &= ~a_F_SEND_TOK;
232 _ANSWER(3, FAL0, TRU1);
233 out.s = NULL;
234 in.s = slp->dat;
235 in.l = slp->datlen;
236 if(!b64_decode(&out, &in)){
237 jebase64:
238 if(out.s != NULL)
239 n_free(out.s);
240 n_err(_("Invalid base64 encoding from GSSAPI server\n"));
241 goto jleave;
243 recv_tok.value = out.s;
244 recv_tok.length = out.l;
245 maj_stat = gss_unwrap(&min_stat, gss_context, &recv_tok, &send_tok,
246 &conf_state, NULL);
247 n_free(out.s);
248 gss_release_buffer(&min_stat, &send_tok);
249 /*f &= ~a_F_SEND_TOK;*/
250 if (maj_stat != GSS_S_COMPLETE) {
251 _smtp_gssapi_error("unwrapping data", maj_stat, min_stat);
252 goto jleave;
255 /* First octet: bit-mask with protection mechanisms (1 = no protection
256 * mechanism).
257 * Second to fourth octet: maximum message size in network byte order.
258 * Fifth and following octets: user name string */
259 in.s = n_autorec_alloc((send_tok.length = 4 + sbp->sb_ccred.cc_user.l) +1);
260 memcpy(in.s + 4, sbp->sb_ccred.cc_user.s, sbp->sb_ccred.cc_user.l +1);
261 in.s[0] = 1;
262 in.s[1] = 0;
263 in.s[2] = in.s[3] = (char)0xFF;
264 send_tok.value = in.s;
265 maj_stat = gss_wrap(&min_stat, gss_context, 0, GSS_C_QOP_DEFAULT, &send_tok,
266 &conf_state, &recv_tok);
267 f |= a_F_RECV_TOK;
268 if (maj_stat != GSS_S_COMPLETE) {
269 _smtp_gssapi_error("wrapping data", maj_stat, min_stat);
270 goto jleave;
273 if(b64_encode_buf(&out, recv_tok.value, recv_tok.length,
274 B64_SALLOC | B64_CRLF) == NULL)
275 goto jleave;
276 _OUT(out.s);
277 _ANSWER(2, FAL0, FAL0);
279 ok = TRU1;
280 jleave:
281 if(f & a_F_RECV_TOK)
282 gss_release_buffer(&min_stat, &recv_tok);
283 if(f & a_F_SEND_TOK)
284 gss_release_buffer(&min_stat, &send_tok);
285 if(f & a_F_TARGET_NAME)
286 gss_release_name(&min_stat, &target_name);
287 if(f & a_F_GSS_CONTEXT)
288 gss_delete_sec_context(&min_stat, &gss_context, GSS_C_NO_BUFFER);
289 NYD_LEAVE;
290 return ok;
293 # ifdef m_DEFINED_GCC_C_NT_HOSTBASED_SERVICE
294 # undef GSS_C_NT_HOSTBASED_SERVICE
295 # endif
296 #endif /* HAVE_GSSAPI */
298 /* s-it-mode */