Fix #262: Adium: SIPE doesn't auto- or re-connect
[siplcs.git] / src / core / sip-sec-negotiate.c
blob44ad43000df0b4db66d87411b736fab01a1cac1d
1 /**
2 * @file sip-sec-negotiate.c
4 * pidgin-sipe
6 * Copyright (C) 2013 SIPE Project <http://sipe.sourceforge.net/>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * Implementation for HTTP "WWW-Authenticate: Negotiate" scheme.
25 * It is a wrapper that will always try Kerberos first and fall back to NTLM.
28 #include <glib.h>
30 #include "sipe-common.h"
31 #include "sip-sec.h"
32 #include "sip-sec-mech.h"
33 #include "sip-sec-gssapi.h" /* for Kerberos */
34 #include "sip-sec-negotiate.h"
35 #include "sip-sec-ntlm.h"
36 #include "sipe-backend.h"
37 #include "sipe-core.h"
39 /* Security context for Negotiate */
40 typedef struct _context_negotiate {
41 struct sip_sec_context common;
42 const gchar *domain;
43 const gchar *username;
44 const gchar *password;
45 SipSecContext krb5;
46 SipSecContext ntlm;
47 } *context_negotiate;
49 #define SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK 0x80000000
51 static void sip_sec_negotiate_drop_krb5(context_negotiate context)
53 if (context->krb5)
54 context->krb5->destroy_context_func(context->krb5);
55 context->krb5 = NULL;
58 static void sip_sec_negotiate_copy_flags(context_negotiate ctx,
59 SipSecContext context)
61 context->flags = ctx->common.flags;
64 static void sip_sec_negotiate_copy_settings(context_negotiate ctx,
65 SipSecContext context)
67 if (context->flags & SIP_SEC_FLAG_COMMON_READY)
68 ctx->common.flags |= SIP_SEC_FLAG_COMMON_READY;
69 else
70 ctx->common.flags &= ~SIP_SEC_FLAG_COMMON_READY;
71 ctx->common.expires = context->expires;
74 static gboolean sip_sec_negotiate_ntlm_fallback(context_negotiate context)
76 if (context->common.flags & SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK) {
77 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_negotiate_ntlm_fallback: forbidden");
78 return(FALSE);
81 sip_sec_negotiate_drop_krb5(context);
82 sip_sec_negotiate_copy_flags(context, context->ntlm);
84 return(context->ntlm->acquire_cred_func(context->ntlm,
85 context->domain,
86 context->username,
87 context->password));
90 /* sip-sec-mech.h API implementation for Negotiate */
92 static gboolean
93 sip_sec_acquire_cred__negotiate(SipSecContext context,
94 const gchar *domain,
95 const gchar *username,
96 const gchar *password)
98 context_negotiate ctx = (context_negotiate) context;
99 gboolean ret;
101 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: entering");
103 ctx->domain = domain;
104 ctx->username = username;
105 ctx->password = password;
107 context = ctx->krb5;
108 sip_sec_negotiate_copy_flags(ctx, context);
109 ret = context->acquire_cred_func(context,
110 domain,
111 username,
112 password);
113 if (!ret) {
114 /* Kerberos failed -> fall back to NTLM immediately */
115 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: fallback to NTLM");
116 ret = sip_sec_negotiate_ntlm_fallback(ctx);
119 return(ret);
122 static gboolean
123 sip_sec_init_sec_context__negotiate(SipSecContext context,
124 SipSecBuffer in_buff,
125 SipSecBuffer *out_buff,
126 const gchar *service_name)
128 context_negotiate ctx = (context_negotiate) context;
129 gboolean ret;
131 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: entering");
133 /* Kerberos available? */
134 context = ctx->krb5;
135 if (context) {
136 ret = context->init_context_func(context,
137 in_buff,
138 out_buff,
139 service_name);
141 if (!ret) {
142 /* Kerberos failed -> fall back to NTLM */
143 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: fallback to NTLM");
144 ret = sip_sec_negotiate_ntlm_fallback(ctx);
146 if (ret) {
147 context = ctx->ntlm;
148 ret = context->init_context_func(context,
149 in_buff,
150 out_buff,
151 service_name);
153 } else {
154 /* Kerberos succeeded -> disable fallback to NTLM */
155 ctx->common.flags |= SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK;
158 /* No Kerberos available -> use NTLM */
159 } else {
160 context = ctx->ntlm;
161 ret = context->init_context_func(context,
162 in_buff,
163 out_buff,
164 service_name);
167 /* context points to the last used child context */
168 if (ret)
169 sip_sec_negotiate_copy_settings(ctx, context);
171 return(ret);
174 static gboolean
175 sip_sec_make_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context,
176 SIPE_UNUSED_PARAMETER const gchar *message,
177 SIPE_UNUSED_PARAMETER SipSecBuffer *signature)
179 /* No implementation needed, as Negotiate is not used for SIP */
180 return(FALSE);
183 static gboolean
184 sip_sec_verify_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context,
185 SIPE_UNUSED_PARAMETER const gchar *message,
186 SIPE_UNUSED_PARAMETER SipSecBuffer signature)
188 /* No implementation needed, as Negotiate is not used for SIP */
189 return(FALSE);
192 static void
193 sip_sec_destroy_sec_context__negotiate(SipSecContext context)
195 context_negotiate ctx = (context_negotiate) context;
197 if (ctx->ntlm)
198 ctx->ntlm->destroy_context_func(ctx->ntlm);
199 sip_sec_negotiate_drop_krb5(ctx);
200 g_free(ctx);
204 * This module doesn't implement SPNEGO (RFC 4559) but instead returns raw
205 * NTLM. Therefore we should not use "Authorization: Negotiate" for NTLM
206 * although Microsoft servers *do* accept them.
208 static const gchar *
209 sip_sec_context_name__negotiate(SipSecContext context)
211 context_negotiate ctx = (context_negotiate) context;
212 if (ctx->krb5)
213 return("Negotiate");
214 else
215 return("NTLM");
218 SipSecContext
219 sip_sec_create_context__negotiate(SIPE_UNUSED_PARAMETER guint type)
221 context_negotiate context = NULL;
222 SipSecContext krb5 = sip_sec_create_context__gssapi(SIPE_AUTHENTICATION_TYPE_KERBEROS);
224 if (krb5) {
225 SipSecContext ntlm = sip_sec_create_context__ntlm(SIPE_AUTHENTICATION_TYPE_NTLM);
227 if (ntlm) {
228 context = g_malloc0(sizeof(struct _context_negotiate));
230 if (context) {
231 context->common.acquire_cred_func = sip_sec_acquire_cred__negotiate;
232 context->common.init_context_func = sip_sec_init_sec_context__negotiate;
233 context->common.destroy_context_func = sip_sec_destroy_sec_context__negotiate;
234 context->common.make_signature_func = sip_sec_make_signature__negotiate;
235 context->common.verify_signature_func = sip_sec_verify_signature__negotiate;
236 context->common.context_name_func = sip_sec_context_name__negotiate;
237 context->krb5 = krb5;
238 context->ntlm = ntlm;
240 krb5->type = SIPE_AUTHENTICATION_TYPE_KERBEROS;
241 ntlm->type = SIPE_AUTHENTICATION_TYPE_NTLM;
243 } else {
244 ntlm->destroy_context_func(ntlm);
248 if (!context) {
249 krb5->destroy_context_func(krb5);
253 return((SipSecContext) context);
257 Local Variables:
258 mode: c
259 c-file-style: "bsd"
260 indent-tabs-mode: t
261 tab-width: 8
262 End: