THANKS: Michael Dressel
[s-mailx.git] / smtp-gssapi.h
bloba3635670c43e02d92c3c3acadbdda2f871dadc94
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 */
8 /* Derived from `imap_gssapi.h', which is:
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.
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 m_DEFINED_GCC_C_NT_HOSTBASED_SERVICE
79 # endif
80 #else
81 # include <gssapi.h>
82 #endif
84 static void _smtp_gssapi_error1(char const *s, OM_uint32 code, int typ);
85 static void _smtp_gssapi_error(char const *s, OM_uint32 maj_stat,
86 OM_uint32 min_stat);
88 static void
89 _smtp_gssapi_error1(char const *s, OM_uint32 code, int typ)
91 OM_uint32 maj_stat, min_stat;
92 gss_buffer_desc msg = GSS_C_EMPTY_BUFFER;
93 OM_uint32 msg_ctx = 0;
94 NYD_ENTER;
96 do {
97 maj_stat = gss_display_status(&min_stat, code, typ, GSS_C_NO_OID,
98 &msg_ctx, &msg);
99 if (maj_stat == GSS_S_COMPLETE) {
100 n_err(_("GSS error: %s / %.*s\n"),
101 s, (int)msg.length, (char*)msg.value);
102 gss_release_buffer(&min_stat, &msg);
103 } else {
104 n_err(_("GSS error: %s / unknown\n"), s);
105 break;
107 } while (msg_ctx);
108 NYD_LEAVE;
111 static void
112 _smtp_gssapi_error(char const *s, OM_uint32 maj_stat, OM_uint32 min_stat)
114 NYD_ENTER;
115 _smtp_gssapi_error1(s, maj_stat, GSS_C_GSS_CODE);
116 _smtp_gssapi_error1(s, min_stat, GSS_C_MECH_CODE);
117 NYD_LEAVE;
120 static bool_t
121 _smtp_gssapi(struct sock *sp, struct sendbundle *sbp, struct smtp_line *slp)
123 struct str in, out;
124 gss_buffer_desc send_tok, recv_tok;
125 gss_name_t target_name;
126 gss_ctx_id_t gss_context;
127 OM_uint32 maj_stat, min_stat, ret_flags;
128 int conf_state;
129 enum{
130 a_F_NONE,
131 a_F_RECV_TOK = 1u<<0,
132 a_F_SEND_TOK = 1u<<1,
133 a_F_TARGET_NAME = 1u<<2,
134 a_F_GSS_CONTEXT = 1u<<3
135 } f;
136 bool_t ok;
137 NYD_ENTER;
139 ok = FAL0;
140 f = a_F_NONE;
142 if(INT_MAX - 1 - 4 <= sbp->sb_ccred.cc_user.l)
143 goto jleave;
145 send_tok.value = n_autorec_alloc(
146 (send_tok.length = sbp->sb_url.url_host.l + 5) +1);
147 memcpy(send_tok.value, "smtp@", 5);
148 memcpy((char*)send_tok.value + 5, sbp->sb_url.url_host.s,
149 sbp->sb_url.url_host.l +1);
150 maj_stat = gss_import_name(&min_stat, &send_tok, GSS_C_NT_HOSTBASED_SERVICE,
151 &target_name);
152 f |= a_F_TARGET_NAME;
153 if (maj_stat != GSS_S_COMPLETE) {
154 _smtp_gssapi_error(savestrbuf(send_tok.value, send_tok.length),
155 maj_stat, min_stat);
156 goto jleave;
159 gss_context = GSS_C_NO_CONTEXT;
160 maj_stat = gss_init_sec_context(&min_stat,
161 GSS_C_NO_CREDENTIAL,
162 &gss_context,
163 target_name,
164 GSS_C_NO_OID,
165 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
167 GSS_C_NO_CHANNEL_BINDINGS,
168 GSS_C_NO_BUFFER,
169 NULL,
170 &send_tok,
171 &ret_flags,
172 NULL);
173 f |= a_F_SEND_TOK | a_F_GSS_CONTEXT;
174 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
175 _smtp_gssapi_error("initializing GSS context", maj_stat, min_stat);
176 goto jleave;
179 _OUT(NETLINE("AUTH GSSAPI"));
180 _ANSWER(3, FAL0, FAL0);
181 while (maj_stat == GSS_S_CONTINUE_NEEDED) {
182 /* Pass token obtained from first gss_init_sec_context() call */
183 if(b64_encode_buf(&out, send_tok.value, send_tok.length,
184 B64_SALLOC | B64_CRLF) == NULL)
185 goto jleave;
186 gss_release_buffer(&min_stat, &send_tok);
187 f &= ~a_F_SEND_TOK;
188 _OUT(out.s);
189 _ANSWER(3, FAL0, TRU1);
191 out.s = NULL;
192 in.s = slp->dat;
193 in.l = slp->datlen;
194 if(!b64_decode(&out, &in))
195 goto jebase64;
196 recv_tok.value = out.s;
197 recv_tok.length = out.l;
198 maj_stat = gss_init_sec_context(&min_stat,
199 GSS_C_NO_CREDENTIAL,
200 &gss_context,
201 target_name,
202 GSS_C_NO_OID,
203 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG,
205 GSS_C_NO_CHANNEL_BINDINGS,
206 &recv_tok,
207 NULL,
208 &send_tok,
209 &ret_flags,
210 NULL);
211 free(out.s);
212 f |= a_F_SEND_TOK;
213 if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
214 _smtp_gssapi_error("initializing context", maj_stat, min_stat);
215 goto jleave;
219 gss_release_name(&min_stat, &target_name);
220 f &= ~a_F_TARGET_NAME;
222 /* Pass token obtained from second gss_init_sec_context() call */
223 if(b64_encode_buf(&out, send_tok.value, send_tok.length,
224 B64_SALLOC | B64_CRLF) == NULL)
225 goto jleave;
226 _OUT(out.s);
228 gss_release_buffer(&min_stat, &send_tok);
229 f &= ~a_F_SEND_TOK;
231 _ANSWER(3, FAL0, TRU1);
232 out.s = NULL;
233 in.s = slp->dat;
234 in.l = slp->datlen;
235 if(!b64_decode(&out, &in)){
236 jebase64:
237 if(out.s != NULL)
238 free(out.s);
239 n_err(_("Invalid base64 encoding from GSSAPI server\n"));
240 goto jleave;
242 recv_tok.value = out.s;
243 recv_tok.length = out.l;
244 maj_stat = gss_unwrap(&min_stat, gss_context, &recv_tok, &send_tok,
245 &conf_state, NULL);
246 free(out.s);
247 gss_release_buffer(&min_stat, &send_tok);
248 /*f &= ~a_F_SEND_TOK;*/
249 if (maj_stat != GSS_S_COMPLETE) {
250 _smtp_gssapi_error("unwrapping data", maj_stat, min_stat);
251 goto jleave;
254 /* First octet: bit-mask with protection mechanisms (1 = no protection
255 * mechanism).
256 * Second to fourth octet: maximum message size in network byte order.
257 * Fifth and following octets: user name string */
258 in.s = n_autorec_alloc((send_tok.length = 4 + sbp->sb_ccred.cc_user.l) +1);
259 memcpy(in.s + 4, sbp->sb_ccred.cc_user.s, sbp->sb_ccred.cc_user.l +1);
260 in.s[0] = 1;
261 in.s[1] = 0;
262 in.s[2] = in.s[3] = (char)0xFF;
263 send_tok.value = in.s;
264 maj_stat = gss_wrap(&min_stat, gss_context, 0, GSS_C_QOP_DEFAULT, &send_tok,
265 &conf_state, &recv_tok);
266 f |= a_F_RECV_TOK;
267 if (maj_stat != GSS_S_COMPLETE) {
268 _smtp_gssapi_error("wrapping data", maj_stat, min_stat);
269 goto jleave;
272 if(b64_encode_buf(&out, recv_tok.value, recv_tok.length,
273 B64_SALLOC | B64_CRLF) == NULL)
274 goto jleave;
275 _OUT(out.s);
276 _ANSWER(2, FAL0, FAL0);
278 ok = TRU1;
279 jleave:
280 if(f & a_F_RECV_TOK)
281 gss_release_buffer(&min_stat, &recv_tok);
282 if(f & a_F_SEND_TOK)
283 gss_release_buffer(&min_stat, &send_tok);
284 if(f & a_F_TARGET_NAME)
285 gss_release_name(&min_stat, &target_name);
286 if(f & a_F_GSS_CONTEXT)
287 gss_delete_sec_context(&min_stat, &gss_context, GSS_C_NO_BUFFER);
288 NYD_LEAVE;
289 return ok;
292 # ifdef m_DEFINED_GCC_C_NT_HOSTBASED_SERVICE
293 # undef GSS_C_NT_HOSTBASED_SERVICE
294 # endif
295 #endif /* HAVE_GSSAPI */
297 /* s-it-mode */