2 * Copyright (C) 2006 Michael Tharp <gxti@partiallystapled.com>
3 * Copyright (C) 2006 charybdis development team
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * 1.Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * 2.Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3.The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
41 #include "s_newconf.h"
44 static int m_authenticate(struct MsgBuf
*, struct Client
*, struct Client
*, int, const char **);
45 static int me_sasl(struct MsgBuf
*, struct Client
*, struct Client
*, int, const char **);
46 static int me_mechlist(struct MsgBuf
*, struct Client
*, struct Client
*, int, const char **);
48 static void abort_sasl(struct Client
*);
49 static void abort_sasl_exit(hook_data_client_exit
*);
51 static void advertise_sasl(struct Client
*);
52 static void advertise_sasl_exit(hook_data_client_exit
*);
54 static unsigned int CLICAP_SASL
= 0;
55 static char mechlist_buf
[BUFSIZE
];
57 struct Message authenticate_msgtab
= {
58 "AUTHENTICATE", 0, 0, 0, 0,
59 {{m_authenticate
, 2}, {m_authenticate
, 2}, mg_ignore
, mg_ignore
, mg_ignore
, {m_authenticate
, 2}}
61 struct Message sasl_msgtab
= {
63 {mg_ignore
, mg_ignore
, mg_ignore
, mg_ignore
, {me_sasl
, 5}, mg_ignore
}
65 struct Message mechlist_msgtab
= {
66 "MECHLIST", 0, 0, 0, 0,
67 {mg_ignore
, mg_ignore
, mg_ignore
, mg_ignore
, {me_mechlist
, 2}, mg_ignore
}
70 mapi_clist_av1 sasl_clist
[] = {
71 &authenticate_msgtab
, &sasl_msgtab
, &mechlist_msgtab
, NULL
73 mapi_hfn_list_av1 sasl_hfnlist
[] = {
74 { "new_local_user", (hookfn
) abort_sasl
},
75 { "client_exit", (hookfn
) abort_sasl_exit
},
76 { "new_remote_user", (hookfn
) advertise_sasl
},
77 { "client_exit", (hookfn
) advertise_sasl_exit
},
82 sasl_visible(struct Client
*client_p
)
84 struct Client
*agent_p
= NULL
;
86 if (ConfigFileEntry
.sasl_service
)
87 agent_p
= find_named_client(ConfigFileEntry
.sasl_service
);
89 return agent_p
!= NULL
&& IsService(agent_p
);
93 sasl_data(struct Client
*client_p
)
95 return *mechlist_buf
!= 0 ? mechlist_buf
: NULL
;
98 static struct ClientCapability capdata_sasl
= {
99 .visible
= sasl_visible
,
101 .flags
= CLICAP_FLAGS_STICKY
,
107 memset(mechlist_buf
, 0, sizeof mechlist_buf
);
108 CLICAP_SASL
= capability_put(cli_capindex
, "sasl", &capdata_sasl
);
115 capability_orphan(cli_capindex
, "sasl");
118 DECLARE_MODULE_AV2(sasl
, _modinit
, _moddeinit
, sasl_clist
, NULL
, sasl_hfnlist
, NULL
, NULL
, NULL
);
121 m_authenticate(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
,
122 int parc
, const char *parv
[])
124 struct Client
*agent_p
= NULL
;
125 struct Client
*saslserv_p
= NULL
;
127 /* They really should use CAP for their own sake. */
128 if(!IsCapable(source_p
, CLICAP_SASL
))
131 if (strlen(client_p
->id
) == 3)
133 exit_client(client_p
, client_p
, client_p
, "Mixing client and server protocol");
137 saslserv_p
= find_named_client(ConfigFileEntry
.sasl_service
);
138 if (saslserv_p
== NULL
|| !IsService(saslserv_p
))
140 sendto_one(source_p
, form_str(ERR_SASLABORTED
), me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
);
144 if(source_p
->localClient
->sasl_complete
)
146 *source_p
->localClient
->sasl_agent
= '\0';
147 source_p
->localClient
->sasl_complete
= 0;
150 if(strlen(parv
[1]) > 400)
152 sendto_one(source_p
, form_str(ERR_SASLTOOLONG
), me
.name
, EmptyString(source_p
->name
) ? "*" : source_p
->name
);
158 /* Allocate a UID. */
159 strcpy(source_p
->id
, generate_uid());
160 add_to_id_hash(source_p
->id
, source_p
);
163 if(*source_p
->localClient
->sasl_agent
)
164 agent_p
= find_id(source_p
->localClient
->sasl_agent
);
168 sendto_one(saslserv_p
, ":%s ENCAP %s SASL %s %s H %s %s",
169 me
.id
, saslserv_p
->servptr
->name
, source_p
->id
, saslserv_p
->id
,
170 source_p
->host
, source_p
->sockhost
);
172 if (source_p
->certfp
!= NULL
)
173 sendto_one(saslserv_p
, ":%s ENCAP %s SASL %s %s S %s %s",
174 me
.id
, saslserv_p
->servptr
->name
, source_p
->id
, saslserv_p
->id
,
175 parv
[1], source_p
->certfp
);
177 sendto_one(saslserv_p
, ":%s ENCAP %s SASL %s %s S %s",
178 me
.id
, saslserv_p
->servptr
->name
, source_p
->id
, saslserv_p
->id
,
181 rb_strlcpy(source_p
->localClient
->sasl_agent
, saslserv_p
->id
, IDLEN
);
184 sendto_one(agent_p
, ":%s ENCAP %s SASL %s %s C %s",
185 me
.id
, agent_p
->servptr
->name
, source_p
->id
, agent_p
->id
,
187 source_p
->localClient
->sasl_out
++;
193 me_sasl(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
,
194 int parc
, const char *parv
[])
196 struct Client
*target_p
, *agent_p
;
198 /* Let propagate if not addressed to us, or if broadcast.
199 * Only SASL agents can answer global requests.
201 if(strncmp(parv
[2], me
.id
, 3))
204 if((target_p
= find_id(parv
[2])) == NULL
)
207 if((agent_p
= find_id(parv
[1])) == NULL
)
210 if(source_p
!= agent_p
->servptr
) /* WTF?! */
213 /* We only accept messages from SASL agents; these must have umode +S
214 * (so the server must be listed in a service{} block).
216 if(!IsService(agent_p
))
219 /* Reject if someone has already answered. */
220 if(*target_p
->localClient
->sasl_agent
&& strncmp(parv
[1], target_p
->localClient
->sasl_agent
, IDLEN
))
222 else if(!*target_p
->localClient
->sasl_agent
)
223 rb_strlcpy(target_p
->localClient
->sasl_agent
, parv
[1], IDLEN
);
226 sendto_one(target_p
, "AUTHENTICATE %s", parv
[4]);
227 else if(*parv
[3] == 'D')
230 sendto_one(target_p
, form_str(ERR_SASLFAIL
), me
.name
, EmptyString(target_p
->name
) ? "*" : target_p
->name
);
231 else if(*parv
[4] == 'S') {
232 sendto_one(target_p
, form_str(RPL_SASLSUCCESS
), me
.name
, EmptyString(target_p
->name
) ? "*" : target_p
->name
);
233 target_p
->localClient
->sasl_complete
= 1;
234 ServerStats
.is_ssuc
++;
236 *target_p
->localClient
->sasl_agent
= '\0'; /* Blank the stored agent so someone else can answer */
238 else if(*parv
[3] == 'M')
239 sendto_one(target_p
, form_str(RPL_SASLMECHS
), me
.name
, EmptyString(target_p
->name
) ? "*" : target_p
->name
, parv
[4]);
245 me_mechlist(struct MsgBuf
*msgbuf_p
, struct Client
*client_p
, struct Client
*source_p
,
246 int parc
, const char *parv
[])
248 rb_strlcpy(mechlist_buf
, parv
[1], sizeof mechlist_buf
);
253 /* If the client never finished authenticating but is
254 * registering anyway, abort the exchange.
257 abort_sasl(struct Client
*data
)
259 if(data
->localClient
->sasl_out
== 0 || data
->localClient
->sasl_complete
)
262 data
->localClient
->sasl_out
= data
->localClient
->sasl_complete
= 0;
263 ServerStats
.is_sbad
++;
266 sendto_one(data
, form_str(ERR_SASLABORTED
), me
.name
, EmptyString(data
->name
) ? "*" : data
->name
);
268 if(*data
->localClient
->sasl_agent
)
270 struct Client
*agent_p
= find_id(data
->localClient
->sasl_agent
);
273 sendto_one(agent_p
, ":%s ENCAP %s SASL %s %s D A", me
.id
, agent_p
->servptr
->name
,
274 data
->id
, agent_p
->id
);
279 sendto_server(NULL
, NULL
, CAP_TS6
|CAP_ENCAP
, NOCAPS
, ":%s ENCAP * SASL %s * D A", me
.id
,
284 abort_sasl_exit(hook_data_client_exit
*data
)
286 if (data
->target
->localClient
)
287 abort_sasl(data
->target
);
291 advertise_sasl(struct Client
*client_p
)
293 if (!ConfigFileEntry
.sasl_service
)
296 if (irccmp(client_p
->name
, ConfigFileEntry
.sasl_service
))
299 sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY
, ":%s CAP * NEW :sasl", me
.name
);
303 advertise_sasl_exit(hook_data_client_exit
*data
)
305 if (!ConfigFileEntry
.sasl_service
)
308 if (irccmp(data
->target
->name
, ConfigFileEntry
.sasl_service
))
311 sendto_local_clients_with_capability(CLICAP_CAP_NOTIFY
, ":%s CAP * DEL :sasl", me
.name
);