update ChangeLog
[siplcs.git] / src / core / sip-sec-negotiate.c
blob14ddb863c26c6891bb0ac3847dc6ad2dbe54cbf8
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-krb5.h"
34 #include "sip-sec-negotiate.h"
35 #include "sip-sec-ntlm.h"
36 #include "sipe-backend.h"
38 /* Security context for Negotiate */
39 typedef struct _context_negotiate {
40 struct sip_sec_context common;
41 const gchar *domain;
42 const gchar *username;
43 const gchar *password;
44 SipSecContext krb5;
45 SipSecContext ntlm;
46 } *context_negotiate;
48 #define SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK 0x80000000
50 static void sip_sec_negotiate_drop_krb5(context_negotiate context)
52 if (context->krb5)
53 context->krb5->destroy_context_func(context->krb5);
54 context->krb5 = NULL;
57 static void sip_sec_negotiate_copy_flags(context_negotiate ctx,
58 SipSecContext context)
60 context->flags = ctx->common.flags;
63 static void sip_sec_negotiate_copy_settings(context_negotiate ctx,
64 SipSecContext context)
66 if (context->flags & SIP_SEC_FLAG_COMMON_READY)
67 ctx->common.flags |= SIP_SEC_FLAG_COMMON_READY;
68 else
69 ctx->common.flags &= ~SIP_SEC_FLAG_COMMON_READY;
70 ctx->common.expires = context->expires;
73 static gboolean sip_sec_negotiate_ntlm_fallback(context_negotiate context)
75 if (context->common.flags & SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK) {
76 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_negotiate_ntlm_fallback: forbidden");
77 return(FALSE);
80 sip_sec_negotiate_drop_krb5(context);
81 sip_sec_negotiate_copy_flags(context, context->ntlm);
83 return(context->ntlm->acquire_cred_func(context->ntlm,
84 context->domain,
85 context->username,
86 context->password));
89 /* sip-sec-mech.h API implementation for Negotiate */
91 static gboolean
92 sip_sec_acquire_cred__negotiate(SipSecContext context,
93 const gchar *domain,
94 const gchar *username,
95 const gchar *password)
97 context_negotiate ctx = (context_negotiate) context;
98 gboolean ret;
100 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: entering");
102 ctx->domain = domain;
103 ctx->username = username;
104 ctx->password = password;
106 context = ctx->krb5;
107 sip_sec_negotiate_copy_flags(ctx, context);
108 ret = context->acquire_cred_func(context,
109 domain,
110 username,
111 password);
112 if (!ret) {
113 /* Kerberos failed -> fall back to NTLM immediately */
114 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: fallback to NTLM");
115 ret = sip_sec_negotiate_ntlm_fallback(ctx);
118 return(ret);
121 static gboolean
122 sip_sec_init_sec_context__negotiate(SipSecContext context,
123 SipSecBuffer in_buff,
124 SipSecBuffer *out_buff,
125 const gchar *service_name)
127 context_negotiate ctx = (context_negotiate) context;
128 gboolean ret;
130 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: entering");
132 /* Kerberos available? */
133 context = ctx->krb5;
134 if (context) {
135 ret = context->init_context_func(context,
136 in_buff,
137 out_buff,
138 service_name);
140 if (!ret) {
141 /* Kerberos failed -> fall back to NTLM */
142 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: fallback to NTLM");
143 ret = sip_sec_negotiate_ntlm_fallback(ctx);
145 if (ret) {
146 context = ctx->ntlm;
147 ret = context->init_context_func(context,
148 in_buff,
149 out_buff,
150 service_name);
152 } else {
153 /* Kerberos succeeded -> disable fallback to NTLM */
154 ctx->common.flags |= SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK;
157 /* No Kerberos available -> use NTLM */
158 } else {
159 context = ctx->ntlm;
160 ret = context->init_context_func(context,
161 in_buff,
162 out_buff,
163 service_name);
166 /* context points to the last used child context */
167 if (ret)
168 sip_sec_negotiate_copy_settings(ctx, context);
170 return(ret);
173 static gboolean
174 sip_sec_make_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context,
175 SIPE_UNUSED_PARAMETER const gchar *message,
176 SIPE_UNUSED_PARAMETER SipSecBuffer *signature)
178 /* No implementation needed, as Negotiate is not used for SIP */
179 return(FALSE);
182 static gboolean
183 sip_sec_verify_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context,
184 SIPE_UNUSED_PARAMETER const gchar *message,
185 SIPE_UNUSED_PARAMETER SipSecBuffer signature)
187 /* No implementation needed, as Negotiate is not used for SIP */
188 return(FALSE);
191 static void
192 sip_sec_destroy_sec_context__negotiate(SipSecContext context)
194 context_negotiate ctx = (context_negotiate) context;
196 if (ctx->ntlm)
197 ctx->ntlm->destroy_context_func(ctx->ntlm);
198 sip_sec_negotiate_drop_krb5(ctx);
199 g_free(ctx);
203 * This module doesn't implement SPNEGO (RFC 4559) but instead returns raw
204 * NTLM. Therefore we should not use "Authorization: Negotiate" for NTLM
205 * although Microsoft servers *do* accept them.
207 static const gchar *
208 sip_sec_context_name__negotiate(SipSecContext context)
210 context_negotiate ctx = (context_negotiate) context;
211 if (ctx->krb5)
212 return("Negotiate");
213 else
214 return("NTLM");
217 SipSecContext
218 sip_sec_create_context__negotiate(guint type)
220 context_negotiate context = NULL;
221 SipSecContext krb5 = sip_sec_create_context__krb5(type);
223 if (krb5) {
224 SipSecContext ntlm = sip_sec_create_context__ntlm(type);
226 if (ntlm) {
227 context = g_malloc0(sizeof(struct _context_negotiate));
229 if (context) {
230 context->common.acquire_cred_func = sip_sec_acquire_cred__negotiate;
231 context->common.init_context_func = sip_sec_init_sec_context__negotiate;
232 context->common.destroy_context_func = sip_sec_destroy_sec_context__negotiate;
233 context->common.make_signature_func = sip_sec_make_signature__negotiate;
234 context->common.verify_signature_func = sip_sec_verify_signature__negotiate;
235 context->common.context_name_func = sip_sec_context_name__negotiate;
236 context->krb5 = krb5;
237 context->ntlm = ntlm;
238 } else {
239 ntlm->destroy_context_func(ntlm);
243 if (!context) {
244 krb5->destroy_context_func(krb5);
248 return((SipSecContext) context);
252 Local Variables:
253 mode: c
254 c-file-style: "bsd"
255 indent-tabs-mode: t
256 tab-width: 8
257 End: