File: sasl_auth

package info (click to toggle)
epic5 3.0.3-1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 5,328 kB
  • sloc: ansic: 75,810; makefile: 648; ruby: 227; python: 215; sh: 78; perl: 13
file content (248 lines) | stat: -rw-r--r-- 8,209 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
if (word(2 $loadinfo()) != [pf]) {
	load -pf $word(1 $loadinfo());
	return;
};

package sasl_auth;

## SASL authentication script for EPIC5.
## Written by zlonix@efnet, public domain.
##
## Version: 1.0 (January, 2014)
##  - Initial roll-out
##
## Version: 1.1 (March, 2014)
##  - Perl is not required anymore
##
## Version: 1.2 (April, 2014)
##  - Fixed bug with '\' in password (rb skered)
##
## Version: 1.3 (September, 2014)
##  - Fixed bug with reconnection (rb skered)

## This script implements SASL authentication, primarily used on
## Freenode network. Currently only PLAIN method is supported.
##
## !!! WARNING  !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!!
##    Never forget that your password may be intercepted, which
##    is also very easy with the only currently implemented
##    method. Because of that consider using SSL connection, and
##    do not use same password for IRC and anything important,
##    it can be easily read by root user on your server.
##
##    DUE TO UNKNOWN REASONS THIS SCRIPT DOESN'T WORK WITH
##    'FLOODPROT' SCRIPT FROM DEFAULT EPIC DISTRIBUTION.
## !!! WARNING  !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!!
##
## All rules are controlled with 'sasl_auth' alias, which take three or
## four arguments: server (you may use asterisks like a pattern, best
## match wins if there is a tie), method of the authentication
## (currently unchecked on input and always resolved to 'PLAIN'), user
## name and optional password.  If you do not provide the password it
## will be prompted upon connection. Prompted password is not saved in
## any variable.
##
## Be aware that there is some difference between 'server' definition
## across hooks and EPIC itself (server to which you issue /server
## command may be different to which you're actually connecting).  You
## may define your server as 'irc.freenode.net' but by DNS round-robin
## mechanism be connected to 'hobana.freenode.net', for example, and
## hooks won't be thrown.  If in any doubt - use wildcards, or define
## your preferred servers in ircII.servers file under your $IRCLIB to
## escape from DNS round-robin, if desired.
##
## Examples:
##   sasl_auth *.freenode.net plain MyNick MyPassowrd
##   sasl_auth *.other.net garbage TestNick
##
## Put those into your ~/.ircrc (or such) file after loading
## sasl_auth.
##
## Take special considiration if your credentials contain special chars
## like '\' or '$', because, if, for example, you're using PF loader to
## load your .ircrc (or whatever script you use with sasl_auth) - those
## will be evaluated, and password "\test$var" may end up just as
## "test".
## 
## If you specify server under /sasl_auth command, but upon connect it
## won't ACK our request (essentialy meaning it doesn't support
## capabilities) or NAK it script will disconnect your from this server.
##
## Limitation and bugs:
##  - only one and insecure method (freenode disabled blowfish support,
##    so, this may never be implemented);
##  - in case of a long base64 string for authentication it may
##    not fit to the maximum message length and authentication
##    will be failed, it can happen if you have very long
##    password;

load capctl;

alias sasl_auth (server, method, user, password, void) {
	if (@server && @method && @user) {
		if (findw($server $sasl_auth.servers) == -1) {
			@ push(sasl_auth.servers $server);
			@ push(sasl_auth.methods $method);
			@ push(sasl_auth.users $user);
			if (@password) {
				@ push(sasl_auth.passwords $password);
				@ push(sasl_auth.interactive 0);
			} else {
				@ push(sasl_auth.passwords INTERACTIVE);
				@ push(sasl_auth.interactive 1);
			};
		};
	};

};

alias sasl_auth.plain (nick, password, void) {
#	echo nick  - $nick;
#	echo pass  - $password;
#	echo xform - $xform("-CTCP +B64" $^\nick\\0$^\nick\\0$^\password);
#	echo xform - $xform("-b64 +ctcp" $xform("-CTCP +B64" $^\nick\\0$^\nick\\0$^\password));
	return $xform("-CTCP +B64" $^\nick\\0$^\nick\\0$^\password);
};

alias sasl_auth.setstate (server, state, void) {
	@ :enc = encode($server);

	if (state != [null]) {
		assign sasl_auth.status.$enc $state;
	} else {
		assign -sasl_auth.status.$enc;
	};
};

alias sasl_auth.getstate (server, void) {
	@ :enc = encode($server);

	return $sasl_auth.status[$enc];
};

on #-server_established 100 '\\\\[$$sasl_auth.servers\\\\] %' {
	@ :server = servername();
	@ :server_enc = encode($server);
	@ :state = sasl_auth.getstate($server);
	@ ::sasl_in_progress[$server_enc] = 1;
	
	if (state == []) {
		sasl_auth.setstate $server req_sent;

		quote CAP REQ :sasl;
	} else {
		xecho -b sasl_auth: server_established: server $server is in a wrong state [$state] (should be <empty>);
		sasl_auth.setstate $server null;
	};
};

on ^odd_server_stuff '\\\\[$$sasl_auth.servers\\\\] CAP % ACK *sasl*' {
	@ :server = servername();
	@ :state = sasl_auth.getstate($server);

	if (state == [req_sent]) {
		sasl_auth.setstate $server meth_sent;
		@ :idx = rmatch($server $sasl_auth.servers)-1;

		quote AUTHENTICATE $toupper($word($idx $sasl_auth.methods));
	} else {
		xecho -b sasl_auth: odd_server_stuff: CAP ACK: server $server \(real: $0\) is in a wrong state [$state] (should be "req_sent");
		sasl_auth.setstate $server null;
	};

};

alias sasl_auth.sendauth (auth, void) {
	quote AUTHENTICATE $auth;
};

on ^odd_server_stuff '\* AUTHENTICATE \+' {
	@ :server = servername();
	@ :state = sasl_auth.getstate($server);

	if (state == [meth_sent]) {
		sasl_auth.setstate $server auth_sent;
		@ :idx = rmatch($server $sasl_auth.servers)-1;
		@ :method = word($idx $sasl_auth.methods);
		@ :nick = word($idx $sasl_auth.users);
		@ :pass = word($idx $sasl_auth.passwords);
		@ :interactive = word($idx $sasl_auth.interactive);
		
		if (interactive == 1) {
			input -noecho "SASL password: " {
				## local variables of the hook are not accessible here
				@ :pass = *0;
				@ :idx = rmatch($servername() $sasl_auth.servers)-1;
				@ :nick = word($idx $sasl_auth.users);
				@ :auth = sasl_auth.plain($nick $pass);
				sasl_auth.sendauth $auth;
			};
		} else {
			@ :auth = sasl_auth.plain($nick $pass);
			sasl_auth.sendauth $auth;
		};
	} else {
		xecho -b sasl_auth: odd_server_stuff: AUTHENTICATE: server $server \(real: $0\) is in a wrong state [$state] (should be "meth_sent");
		sasl_auth.setstate $server null;
	};
};

on -901 '\\\\[$$sasl_auth.servers\\\\] *' {
	@ :server = servername();
	@ :state = sasl_auth.getstate($server);

	if (state == [auth_sent]) {
		sasl_auth.setstate $server null;
		disconnect;
	} else {
		xecho -b sasl_auth: 901: server $server \(real: $0\) is in a wrong state [$state] (should be "auth_sent");
		sasl_auth.setstate $server null;
	};
};

on -903 '\\\\[$$sasl_auth.servers\\\\] *' {
	@ :server = servername();
	@ :state = sasl_auth.getstate($server);

	if (state == [auth_sent]) {
		sasl_auth.setstate $server null;
#		quote CAP END;
	} else {
		xecho -b sasl_auth: 903: server $server \(real: $0\) is in a wrong state [$state] (should be "auth_sent");
		sasl_auth.setstate $server null;
	};
};

on #-001 100 '\\\\[$$sasl_auth.servers\\\\] *' {
	@ :server = servername();
	@ :state = sasl_auth.getstate($server);
	
	if (state != []) {
		xecho -b sasl_auth: 001: server $server \(real: $0\) in a state [$state] and do not support capabilities (001 received without ACK/NAK);
		xecho -b sasl_auth: odd_server_stuff: CAP NAK: disconnecting from $server \(real: $0\);
		disconnect;
	};

	sasl_auth.setstate $server null;
};

on ^odd_server_stuff '\\\\[$$sasl_auth.servers\\\\] CAP * NAK *sasl*' {
	@ :server = servername();
	@ :state = sasl_auth.getstate($server);
	
	xecho -b sasl_auth: odd_server_stuff: CAP NAK: server $server \(real: $0\) in a state [$state] and do not support proposed SASL capability (:sasl), NAK received;
	xecho -b sasl_auth: odd_server_stuff: CAP NAK: disconnecting from $server \(real: $0\);

	sasl_auth.setstate $server null;
	disconnect;
};

on #-server_lost 100 '% \\\\[$$sasl_auth.servers\\\\] *' {
	@ :server = servername();
	@ :state = sasl_auth.getstate($server);
	
	if (state != []) {
		xecho -b sasl_auth: server_lost: server $server \(real: $1\) in a wrong state [$state] (should be <empty>);
	};
	sasl_auth.setstate $server null;
};