1 /*
2  * Channel Is Secure UnrealIRCd module (Channel Mode +Z)
3  * (C) Copyright 2010 Bram Matthys (Syzop) and the UnrealIRCd team
4  *
5  * This module will indicate if a channel is secure, and if so will set +Z.
6  * Secure is defined as: all users on the channel are connected through SSL/TLS
7  * Additionally, the channel has to be +z (only allow secure users to join).
8  * Suggested on http://bugs.unrealircd.org/view.php?id=3720
9  * Thanks go to fez for pushing us for some kind of method to indicate
10  * this 'secure channel state', and to Stealth for suggesting this method.
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 1, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  */
26 
27 #include "config.h"
28 #include "struct.h"
29 #include "common.h"
30 #include "sys.h"
31 #include "numeric.h"
32 #include "msg.h"
33 #include "proto.h"
34 #include "channel.h"
35 #include <time.h>
36 #include <sys/stat.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #ifdef _WIN32
41 #include <io.h>
42 #endif
43 #include <fcntl.h>
44 #include "h.h"
45 #ifdef STRIPBADWORDS
46 #include "badwords.h"
47 #endif
48 #ifdef _WIN32
49 #include "version.h"
50 #endif
51 
52 DLLFUNC CMD_FUNC(m_issecure);
53 
54 ModuleHeader MOD_HEADER(m_issecure)
55   = {
56 	"m_issecure",
57 	"$Id$",
58 	"Channel Mode +Z",
59 	"3.2-b8-1",
60 	NULL
61     };
62 
63 Cmode_t EXTCMODE_ISSECURE;
64 
65 #define IsSecureJoin(chptr) (chptr->mode.mode & MODE_ONLYSECURE)
66 #define IsSecureChanIndicated(chptr)	(chptr->mode.extmode & EXTCMODE_ISSECURE)
67 
68 
69 int modeZ_is_ok(aClient *sptr, aChannel *chptr, char *para, int checkt, int what);
70 DLLFUNC int issecure_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *parv[]);
71 DLLFUNC int issecure_part(aClient *cptr, aClient *sptr, aChannel *chptr, char *comment);
72 DLLFUNC int issecure_quit(aClient *acptr, char *comment);
73 DLLFUNC int issecure_kick(aClient *cptr, aClient *sptr, aClient *acptr, aChannel *chptr, char *comment);
74 DLLFUNC int issecure_chanmode(aClient *cptr, aClient *sptr, aChannel *chptr,
75                              char *modebuf, char *parabuf, int sendts, int samode);
76 
77 
MOD_TEST(m_issecure)78 DLLFUNC int MOD_TEST(m_issecure)(ModuleInfo *modinfo)
79 {
80 	return MOD_SUCCESS;
81 }
82 
MOD_INIT(m_issecure)83 DLLFUNC int MOD_INIT(m_issecure)(ModuleInfo *modinfo)
84 {
85 CmodeInfo req;
86 
87 	/* Channel mode */
88 	memset(&req, 0, sizeof(req));
89 	req.paracount = 0;
90 	req.is_ok = modeZ_is_ok;
91 	req.flag = 'Z';
92 	req.local = 1; /* local channel mode */
93 	CmodeAdd(modinfo->handle, req, &EXTCMODE_ISSECURE);
94 
95 	HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_JOIN, issecure_join);
96 	HookAddEx(modinfo->handle, HOOKTYPE_REMOTE_JOIN, issecure_join);
97 	HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_PART, issecure_part);
98 	HookAddEx(modinfo->handle, HOOKTYPE_REMOTE_PART, issecure_part);
99 	HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_QUIT, issecure_quit);
100 	HookAddEx(modinfo->handle, HOOKTYPE_REMOTE_QUIT, issecure_quit);
101 	HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_KICK, issecure_kick);
102 	HookAddEx(modinfo->handle, HOOKTYPE_REMOTE_KICK, issecure_kick);
103 	HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_CHANMODE, issecure_chanmode);
104 	HookAddEx(modinfo->handle, HOOKTYPE_REMOTE_CHANMODE, issecure_chanmode);
105 
106 	MARK_AS_OFFICIAL_MODULE(modinfo);
107 	return MOD_SUCCESS;
108 }
109 
MOD_LOAD(m_issecure)110 DLLFUNC int MOD_LOAD(m_issecure)(int module_load)
111 {
112 	return MOD_SUCCESS;
113 }
114 
MOD_UNLOAD(m_issecure)115 DLLFUNC int MOD_UNLOAD(m_issecure)(int module_unload)
116 {
117 	return MOD_SUCCESS;
118 }
119 
modeZ_is_ok(aClient * sptr,aChannel * chptr,char * para,int checkt,int what)120 int modeZ_is_ok(aClient *sptr, aChannel *chptr, char *para, int checkt, int what)
121 {
122 	/* Reject any attempt to set or unset our mode. Even to IRCOps */
123 	return EX_ALWAYS_DENY;
124 }
125 
channel_has_insecure_users_butone(aChannel * chptr,aClient * skip)126 int channel_has_insecure_users_butone(aChannel *chptr, aClient *skip)
127 {
128 Member *member;
129 
130 	for (member = chptr->members; member; member = member->next)
131 	{
132 		if (member->cptr == skip)
133 			continue;
134 		if (IsULine(member->cptr))
135 			continue;
136 		if (!IsSecureConnect(member->cptr))
137 			return 1;
138 	}
139 	return 0;
140 }
141 
142 #define channel_has_insecure_users(x) channel_has_insecure_users_butone(x, NULL)
143 
144 /* Set channel status of 'chptr' to be no longer secure (-Z) due to 'sptr'.
145  * sptr MAY be null!
146  */
issecure_unset(aChannel * chptr,aClient * sptr,int notice)147 void issecure_unset(aChannel *chptr, aClient *sptr, int notice)
148 {
149 	if (notice)
150 	{
151 		if (chptr->mode.mode & MODE_AUDITORIUM)
152 			sendto_channel_butserv(chptr, &me, ":%s NOTICE %s :User joined and is not connected through SSL, setting channel -Z (insecure)",
153 				me.name, chptr->chname);
154 		else
155 			sendto_channel_butserv(chptr, &me, ":%s NOTICE %s :User '%s' joined and is not connected through SSL, setting channel -Z (insecure)",
156 				me.name, chptr->chname, sptr->name);
157 	}
158 
159 	chptr->mode.extmode &= ~EXTCMODE_ISSECURE;
160 	sendto_channel_butserv(chptr, &me, ":%s MODE %s -Z", me.name, chptr->chname);
161 }
162 
163 
164 /* Set channel status of 'chptr' to be secure (+Z).
165  * Channel might have been insecure (or might not have been +z) and is
166  * now considered secure. If 'sptr' is non-NULL then we are now secure
167  * thanks to this user leaving the chat.
168  */
issecure_set(aChannel * chptr,aClient * sptr,int notice)169 void issecure_set(aChannel *chptr, aClient *sptr, int notice)
170 {
171 	if (notice && sptr)
172 	{
173 		/* note that we have to skip 'sptr', since when this call is being made
174 		 * he is still considered a member of this channel.
175 		 */
176 		sendto_channel_butserv_butone(chptr, &me, sptr, ":%s NOTICE %s :Now all users in the channel are connected through SSL, setting channel +Z (secure)",
177 			me.name, chptr->chname);
178 	} else if (notice)
179 	{
180 		/* note the missing word 'now' in next line */
181 		sendto_channel_butserv(chptr, &me, ":%s NOTICE %s :All users in the channel are connected through SSL, setting channel +Z (secure)",
182 			me.name, chptr->chname);
183 	}
184 	chptr->mode.extmode |= EXTCMODE_ISSECURE;
185 	sendto_channel_butserv_butone(chptr, &me, sptr, ":%s MODE %s +Z", me.name, chptr->chname);
186 }
187 
188 /* Note: the routines below (notably the 'if's) are written with speed in mind,
189  *       so while they can be written shorter, they would only take longer to execute!
190  */
191 
issecure_join(aClient * cptr,aClient * sptr,aChannel * chptr,char * parv[])192 DLLFUNC int issecure_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *parv[])
193 {
194 	/* Only care if chan already +zZ and the user joining is insecure (no need to count) */
195 	if (IsSecureJoin(chptr) && IsSecureChanIndicated(chptr) && !IsSecureConnect(sptr) && !IsULine(sptr))
196 		issecure_unset(chptr, sptr, 1);
197 	return 0;
198 }
199 
issecure_part(aClient * cptr,aClient * sptr,aChannel * chptr,char * comment)200 DLLFUNC int issecure_part(aClient *cptr, aClient *sptr, aChannel *chptr, char *comment)
201 {
202 	/* Only care if chan is +z-Z and the user leaving is insecure, then count */
203 	if (IsSecureJoin(chptr) && !IsSecureChanIndicated(chptr) && !IsSecureConnect(sptr) &&
204 	    !channel_has_insecure_users_butone(chptr, sptr))
205 		issecure_set(chptr, sptr, 1);
206 	return 0;
207 }
208 
issecure_quit(aClient * sptr,char * comment)209 DLLFUNC int issecure_quit(aClient *sptr, char *comment)
210 {
211 Membership *membership;
212 aChannel *chptr;
213 
214 	for (membership = sptr->user->channel; membership; membership=membership->next)
215 	{
216 		chptr = membership->chptr;
217 		/* Identical to part */
218 		if (IsSecureJoin(chptr) && !IsSecureChanIndicated(chptr) &&
219 		    !IsSecureConnect(sptr) && !channel_has_insecure_users_butone(chptr, sptr))
220 			issecure_set(chptr, sptr, 1);
221 	}
222 	return 0;
223 }
224 
issecure_kick(aClient * cptr,aClient * sptr,aClient * victim,aChannel * chptr,char * comment)225 DLLFUNC int issecure_kick(aClient *cptr, aClient *sptr, aClient *victim, aChannel *chptr, char *comment)
226 {
227 	/* Identical to part&quit, except we care about 'victim' and not 'sptr' */
228 	if (IsSecureJoin(chptr) && !IsSecureChanIndicated(chptr) &&
229 	    !IsSecureConnect(victim) && !channel_has_insecure_users_butone(chptr, victim))
230 		issecure_set(chptr, victim, 1);
231 	return 0;
232 }
233 
issecure_chanmode(aClient * cptr,aClient * sptr,aChannel * chptr,char * modebuf,char * parabuf,int sendts,int samode)234 DLLFUNC int issecure_chanmode(aClient *cptr, aClient *sptr, aChannel *chptr,
235                              char *modebuf, char *parabuf, int sendts, int samode)
236 {
237 	if (!strchr(modebuf, 'z'))
238 		return 0; /* don't care */
239 
240 	if (IsSecureJoin(chptr))
241 	{
242 		/* +z is set, check if we need to +Z
243 		 * Note that we need to be careful as there is a possibility that we got here
244 		 * but the channel is ALREADY +z. Due to server2server MODE's.
245 		 */
246 		if (channel_has_insecure_users(chptr))
247 		{
248 			/* Should be -Z, if not already */
249 			if (IsSecureChanIndicated(chptr))
250 				issecure_unset(chptr, NULL, 0); /* would be odd if we got here ;) */
251 		} else {
252 			/* Should be +Z, but check if it isn't already.. */
253 			if (!IsSecureChanIndicated(chptr))
254 				issecure_set(chptr, NULL, 0);
255 		}
256 	} else {
257 		/* there was a -z, check if the channel is currently +Z and if so, set it -Z */
258 		if (IsSecureChanIndicated(chptr))
259 			issecure_unset(chptr, NULL, 0);
260 	}
261 	return 0;
262 }
263