Fix #193: Pidgin Status changes stop working (III)
[siplcs.git] / src / core / sip-sec-negotiate.c
blob015b96ec394c13345a310492eddc6be6d4e9ee82
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 ctx->common.expires = context->expires;
71 static gboolean sip_sec_negotiate_ntlm_fallback(context_negotiate context)
73 if (context->common.flags & SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK) {
74 SIPE_DEBUG_ERROR_NOFORMAT("sip_sec_negotiate_ntlm_fallback: forbidden");
75 return(FALSE);
78 sip_sec_negotiate_drop_krb5(context);
79 sip_sec_negotiate_copy_flags(context, context->ntlm);
81 return(context->ntlm->acquire_cred_func(context->ntlm,
82 context->domain,
83 context->username,
84 context->password));
87 /* sip-sec-mech.h API implementation for Negotiate */
89 static gboolean
90 sip_sec_acquire_cred__negotiate(SipSecContext context,
91 const gchar *domain,
92 const gchar *username,
93 const gchar *password)
95 context_negotiate ctx = (context_negotiate) context;
96 gboolean ret;
98 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: entering");
100 ctx->domain = domain;
101 ctx->username = username;
102 ctx->password = password;
104 context = ctx->krb5;
105 sip_sec_negotiate_copy_flags(ctx, context);
106 ret = context->acquire_cred_func(context,
107 domain,
108 username,
109 password);
110 if (!ret) {
111 /* Kerberos failed -> fall back to NTLM immediately */
112 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_acquire_cred__negotiate: fallback to NTLM");
113 ret = sip_sec_negotiate_ntlm_fallback(ctx);
116 return(ret);
119 static gboolean
120 sip_sec_init_sec_context__negotiate(SipSecContext context,
121 SipSecBuffer in_buff,
122 SipSecBuffer *out_buff,
123 const gchar *service_name)
125 context_negotiate ctx = (context_negotiate) context;
126 gboolean ret;
128 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: entering");
130 /* Kerberos available? */
131 context = ctx->krb5;
132 if (context) {
133 ret = context->init_context_func(context,
134 in_buff,
135 out_buff,
136 service_name);
138 if (!ret) {
139 /* Kerberos failed -> fall back to NTLM */
140 SIPE_DEBUG_INFO_NOFORMAT("sip_sec_init_sec_context__negotiate: fallback to NTLM");
141 ret = sip_sec_negotiate_ntlm_fallback(ctx);
143 if (ret) {
144 context = ctx->ntlm;
145 ret = context->init_context_func(context,
146 in_buff,
147 out_buff,
148 service_name);
150 } else {
151 /* Kerberos succeeded -> disable fallback to NTLM */
152 ctx->common.flags |= SIP_SEC_FLAG_NEGOTIATE_DISABLE_FALLBACK;
155 /* No Kerberos available -> use NTLM */
156 } else {
157 context = ctx->ntlm;
158 ret = context->init_context_func(context,
159 in_buff,
160 out_buff,
161 service_name);
164 /* context points to the last used child context */
165 if (ret)
166 sip_sec_negotiate_copy_settings(ctx, context);
168 return(ret);
171 static gboolean
172 sip_sec_make_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context,
173 SIPE_UNUSED_PARAMETER const gchar *message,
174 SIPE_UNUSED_PARAMETER SipSecBuffer *signature)
176 /* No implementation needed, as Negotiate is not used for SIP */
177 return(FALSE);
180 static gboolean
181 sip_sec_verify_signature__negotiate(SIPE_UNUSED_PARAMETER SipSecContext context,
182 SIPE_UNUSED_PARAMETER const gchar *message,
183 SIPE_UNUSED_PARAMETER SipSecBuffer signature)
185 /* No implementation needed, as Negotiate is not used for SIP */
186 return(FALSE);
189 static void
190 sip_sec_destroy_sec_context__negotiate(SipSecContext context)
192 context_negotiate ctx = (context_negotiate) context;
194 if (ctx->ntlm)
195 ctx->ntlm->destroy_context_func(ctx->ntlm);
196 sip_sec_negotiate_drop_krb5(ctx);
197 g_free(ctx);
200 SipSecContext
201 sip_sec_create_context__negotiate(guint type)
203 context_negotiate context = NULL;
204 SipSecContext krb5 = sip_sec_create_context__krb5(type);
206 if (krb5) {
207 SipSecContext ntlm = sip_sec_create_context__ntlm(type);
209 if (ntlm) {
210 context = g_malloc0(sizeof(struct _context_negotiate));
212 if (context) {
213 context->common.acquire_cred_func = sip_sec_acquire_cred__negotiate;
214 context->common.init_context_func = sip_sec_init_sec_context__negotiate;
215 context->common.destroy_context_func = sip_sec_destroy_sec_context__negotiate;
216 context->common.make_signature_func = sip_sec_make_signature__negotiate;
217 context->common.verify_signature_func = sip_sec_verify_signature__negotiate;
218 context->krb5 = krb5;
219 context->ntlm = ntlm;
220 } else {
221 ntlm->destroy_context_func(ntlm);
225 if (!context) {
226 krb5->destroy_context_func(krb5);
230 return((SipSecContext) context);
234 Local Variables:
235 mode: c
236 c-file-style: "bsd"
237 indent-tabs-mode: t
238 tab-width: 8
239 End: