summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xMakefile5
-rwxr-xr-xTODO4
-rwxr-xr-xvchat-client.c13
-rwxr-xr-xvchat-config.h6
-rwxr-xr-xvchat-howto23
-rwxr-xr-xvchat-protocol.c372
-rwxr-xr-xvchat-ssl.c467
-rwxr-xr-xvchat-ssl.h53
-rwxr-xr-xvchat-ui.c3
-rwxr-xr-xvchat.h2
10 files changed, 642 insertions, 306 deletions
diff --git a/Makefile b/Makefile
index 216f161..81c4fbe 100755
--- a/Makefile
+++ b/Makefile
@@ -30,7 +30,7 @@ CFLAGS += $(OLDREADLINE)
30PREFIX=/usr/local 30PREFIX=/usr/local
31 31
32LIBS = -lreadline -lncurses -lssl -lcrypto 32LIBS = -lreadline -lncurses -lssl -lcrypto
33OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o 33OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-ssl.o
34 34
35 35
36############################################## 36##############################################
@@ -83,5 +83,8 @@ vchat-user.o: vchat-user.c vchat.h
83vchat-commands.o: vchat-commands.c vchat.h vchat-config.h 83vchat-commands.o: vchat-commands.c vchat.h vchat-config.h
84 $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c 84 $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c
85 85
86vchat-ssl.o: vchat-ssl.c vchat-ssl.h
87 $(CC) $(CFLAGS) -o vchat-ssl.o -c vchat-ssl.c
88
86#vchat-client.1: vchat-client.sgml 89#vchat-client.1: vchat-client.sgml
87# docbook2man vchat-client.sgml 90# docbook2man vchat-client.sgml
diff --git a/TODO b/TODO
index ef91851..6e9f4df 100755
--- a/TODO
+++ b/TODO
@@ -1,3 +1,7 @@
1
2- SSL: print SSL fingerprint and let the user decide wether it's authentic
3 and store it to a fingerprint cache or not.
4
1- more documentation 5- more documentation
2 6
3- .i with nickchanges 7- .i with nickchanges
diff --git a/vchat-client.c b/vchat-client.c
index e0fc214..56cc721 100755
--- a/vchat-client.c
+++ b/vchat-client.c
@@ -25,7 +25,6 @@
25#include <fcntl.h> 25#include <fcntl.h>
26#include <signal.h> 26#include <signal.h>
27#include <readline/readline.h> 27#include <readline/readline.h>
28#include <openssl/ssl.h>
29#ifndef NO_LOCALE 28#ifndef NO_LOCALE
30#include <locale.h> 29#include <locale.h>
31#endif 30#endif
@@ -509,15 +508,11 @@ main (int argc, char **argv)
509 loadformats(GLOBAL_FORMAT_FILE); 508 loadformats(GLOBAL_FORMAT_FILE);
510 loadformats(getstroption (CF_FORMFILE)); 509 loadformats(getstroption (CF_FORMFILE));
511 510
512 if (!getintoption(CF_USESSL)) { 511 if (!getintoption(CF_USESSL))
513 setstroption(CF_SERVERHOST,"localhost"); 512 setstroption(CF_SERVERPORT,"2323");
514 setstroption(CF_SERVERPORT,"2323"); 513 else
515 } else { 514 setstroption(CF_SERVERPORT,"2325");
516 SSL_library_init ();
517 SSL_load_error_strings ();
518 }
519 515
520
521 /* install signal handler */ 516 /* install signal handler */
522 signal (SIGINT, cleanup); 517 signal (SIGINT, cleanup);
523 signal (SIGHUP, cleanup); 518 signal (SIGHUP, cleanup);
diff --git a/vchat-config.h b/vchat-config.h
index e2bbc40..eb0afc1 100755
--- a/vchat-config.h
+++ b/vchat-config.h
@@ -24,14 +24,15 @@
24 24
25/* configuration array with structure as defined in vchat.h */ 25/* configuration array with structure as defined in vchat.h */
26extern unsigned int usessl; 26extern unsigned int usessl;
27extern unsigned int ignssl;
27extern unsigned int usetime; 28extern unsigned int usetime;
28extern unsigned int hscroll; 29extern unsigned int hscroll;
29 30
30static volatile configoption configoptions[] = { 31static volatile configoption configoptions[] = {
31/* config-option type name in file default value value localvar */ 32/* config-option type name in file default value value localvar */
32 {CF_NICK, CO_STR, "nick", NULL, NULL, { .pstr = &nick } }, 33 {CF_NICK, CO_STR, "nick", NULL, NULL, { .pstr = &nick } },
33 {CF_FROM, CO_STR, "from", "vc-alpha-0.16", NULL, { NULL } }, 34 {CF_FROM, CO_STR, "from", "vc-alpha-0.17", NULL, { NULL } },
34 {CF_SERVERHOST, CO_STR, "host", "pulse.flatline.de", NULL, { NULL } }, 35 {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } },
35 {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, 36 {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } },
36 {CF_CIPHERSUITE, CO_STR, "ciphers", "HIGH:MEDIUM", NULL, { NULL } }, 37 {CF_CIPHERSUITE, CO_STR, "ciphers", "HIGH:MEDIUM", NULL, { NULL } },
37 {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, 38 {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } },
@@ -41,6 +42,7 @@ static volatile configoption configoptions[] = {
41 {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, 42 {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } },
42 {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, 43 {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }},
43 {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { .pint = &usessl } }, 44 {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { .pint = &usessl } },
45 {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { .pint = &ignssl } },
44 {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, 46 {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } },
45 {CF_USETIME, CO_INT, "usetime", (char *) 1, (char *)-1, { .pint = &usetime } }, 47 {CF_USETIME, CO_INT, "usetime", (char *) 1, (char *)-1, { .pint = &usetime } },
46 {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, 48 {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } },
diff --git a/vchat-howto b/vchat-howto
index da9ac6f..22c34b7 100755
--- a/vchat-howto
+++ b/vchat-howto
@@ -70,6 +70,29 @@ type:
70 70
71$ echo host=vchat.berlin.ccc.de >> ~/.vchat/config 71$ echo host=vchat.berlin.ccc.de >> ~/.vchat/config
72 72
73If you want to ignore SSL-warnings due to missing CA-files, type:
74
75$ echo ignssl=1 >> ~/.vchat/config
76
77If you don't want to ignore SSL-warnings, get the root-certificates from:
78
79 http://www.cacert.org/certs/class3.txt
80 and
81 http://www.cacert.org/certs/root.txt
82
83and copy them into your openssl-certs directory. For example:
84
85 # cp root.txt /etc/ssl/certs/
86 # cp class3.txt /etc/ssl/certs/
87 # cd /etc/ssl/certs
88 # ln -s root.txt `openssl x509 -in root.txt -hash | head -n 1`.0
89 # ln -s class3.txt `openssl x509 -in class3.txt -hash | head -n 1`.0
90
91Now you can type:
92
93 $ echo ignssl=0 >> ~/.vchat/config
94
95
73If you want a seperate private message window, type: 96If you want a seperate private message window, type:
74 97
75$ echo messages=10 >> ~/.vchat/config 98$ echo messages=10 >> ~/.vchat/config
diff --git a/vchat-protocol.c b/vchat-protocol.c
index fc89cf3..274e856 100755
--- a/vchat-protocol.c
+++ b/vchat-protocol.c
@@ -33,6 +33,7 @@
33 33
34/* local includes */ 34/* local includes */
35#include "vchat.h" 35#include "vchat.h"
36#include "vchat-ssl.h"
36 37
37/* version of this module */ 38/* version of this module */
38char *vchat_io_version = "$Id$"; 39char *vchat_io_version = "$Id$";
@@ -43,14 +44,7 @@ unsigned int usingcert = 1;
43 44
44/* locally global variables */ 45/* locally global variables */
45/* SSL-connection */ 46/* SSL-connection */
46static SSL *sslconn = NULL; 47static BIO *sslconn = NULL;
47
48/* declaration of an OpenSSL function which isn't exported by the includes,
49 * but by the library. we use it to set the accepted list of ciphers */
50STACK_OF(SSL_CIPHER) * ssl_create_cipher_list (const SSL_METHOD * meth, STACK_OF (SSL_CIPHER) ** pref, STACK_OF (SSL_CIPHER) ** sorted, const unsigned char *rule_str);
51
52static char *sslpubkey = NULL; /* servers public key extracted from X.509 certificate */
53static int sslpubkeybits = 0; /* length of server public key */
54 48
55/* declaration of local helper functions */ 49/* declaration of local helper functions */
56static void usersignon (char *); 50static void usersignon (char *);
@@ -80,281 +74,90 @@ static int getportnum (char *port);
80extern int status; 74extern int status;
81 75
82int usessl = 1; 76int usessl = 1;
77int ignssl = 0;
83char *encoding; 78char *encoding;
84 79
85/* connects to server */ 80/* connects to server */
86int 81int
87vcconnect (char *server, char *port) 82vcconnect (char *server, char *port)
88{ 83{
89 /* used for tilde expansion of cert & key filenames */ 84 /* used for tilde expansion of cert & key filenames */
90 char *tildex = NULL; 85 char *tildex = NULL;
91 /* buffer for X.509 subject of server certificate */
92 char subjbuf[256];
93 /* variables used to split the subject */
94 char *subjv = NULL;
95 char *subjn = NULL;
96 char *subjc = NULL;
97 char *subjh = NULL;
98 /* pointer to key in certificate */
99 EVP_PKEY *certpubkey = NULL;
100 /* temporary result */
101 int result;
102 /* protocol independent server addresses */
103 struct addrinfo hints, *addr, *tmpaddr;
104 /* SSL-context */
105 SSL_CTX *sslctx = NULL;
106 /* SSL server certificate */
107 X509 *sslserv = NULL;
108 /* SSL method function */
109 SSL_METHOD *sslmeth = NULL;
110
111 /* pointer to tilde-expanded certificate/keyfile-names */
112 char *certfile = NULL, *keyfile = NULL;
113
114 /* variable for verify return */
115 long verify;
116
117 memset( &hints, 0, sizeof(hints));
118 /* Expect v4 and v6 */
119 hints.ai_family = PF_UNSPEC;
120 hints.ai_socktype = SOCK_STREAM;
121 if( getaddrinfo( server, port, &hints, &addr ))
122 return 0;
123 for( tmpaddr = addr; addr; addr = addr->ai_next )
124 {
125 if( (serverfd = socket( addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0)
126 continue;
127 if( connect( serverfd, addr->ai_addr, addr->ai_addrlen ) < 0)
128 {
129 close( serverfd );
130 serverfd = -1;
131 continue;
132 }
133 break;
134 }
135 if( serverfd < 0 )
136 return 0;
137 freeaddrinfo( tmpaddr );
138 86
139 /* inform user */ 87 /* vchat connection x509 store */
140 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, getportnum(port)); 88 vc_x509store_t vc_store;
141 writechan (tmpstr);
142 89
143 usessl = getintoption(CF_USESSL); 90 /* SSL-context */
91 SSL_CTX *sslctx = NULL;
144 92
145 /* do we want to use SSL? */ 93 /* pointer to tilde-expanded certificate/keyfile-names */
146 if (usessl) 94 char *certfile = NULL, *keyfile = NULL;
147 {
148 /* set ssl method -> SSLv2, SSLv3 & TLSv1 client */
149 sslmeth = SSLv23_client_method ();
150 /* generate new SSL context */
151 sslctx = SSL_CTX_new (sslmeth);
152
153 /* set passphrase-callback to own function from vchat-ui.c */
154 SSL_CTX_set_default_passwd_cb (sslctx, passprompt);
155
156 /* set our list of accepted ciphers */
157 ssl_create_cipher_list (sslctx->method, &(sslctx->cipher_list), &(sslctx->cipher_list_by_id), (unsigned char*)getstroption (CF_CIPHERSUITE));
158
159 /* get name of certificate file */
160 certfile = getstroption (CF_CERTFILE);
161
162 /* do we have a certificate file? */
163 if (certfile)
164 {
165 /* does the filename start with a tilde? expand it! */
166 if (certfile[0] == '~')
167 tildex = tilde_expand (certfile);
168 else
169 tildex = certfile;
170
171 if (usingcert) {
172 /* try to load certificate */
173 result = SSL_CTX_use_certificate_file (sslctx, tildex, SSL_FILETYPE_PEM);
174 if (!result)
175 {
176 /* failed, inform user */
177 snprintf (tmpstr, TMPSTRSIZE, "!! Loading user certificate fails: %s", ERR_error_string (ERR_get_error (), NULL));
178 writecf (FS_ERR,tmpstr);
179 }
180 else
181 {
182 /* get name of key file */
183 keyfile = getstroption (CF_KEYFILE);
184
185 /* if we don't have a key file, the key may be in the cert file */
186 if (!keyfile)
187 keyfile = certfile;
188
189 /* does the filename start with a tilde? expand it! */
190 if (keyfile[0] == '~')
191 tildex = tilde_expand (keyfile);
192 else
193 tildex = keyfile;
194
195 /* try to load key (automatically asks for passphrase, if encrypted */
196 result = SSL_CTX_use_PrivateKey_file (sslctx, tildex, SSL_FILETYPE_PEM);
197 if (!result)
198 {
199 /* failed, inform user */
200 snprintf (tmpstr, TMPSTRSIZE, "!! Loading private key fails: %s", ERR_error_string (ERR_get_error (), NULL));
201 writecf (FS_ERR,tmpstr);
202 }
203 else
204 {
205 /* check if OpenSSL thinks key & cert belong together */
206 result = SSL_CTX_check_private_key (sslctx);
207 if (!result)
208 {
209 /* they don't, inform user */
210 snprintf (tmpstr, TMPSTRSIZE, "!! Verifying key and certificate fails: %s", ERR_error_string (ERR_get_error (), NULL));
211 writecf (FS_ERR,tmpstr);
212 }
213 }
214 }
215 }
216 }
217
218 /* don't worry to much about servers X.509 certificate chain */
219 SSL_CTX_set_verify_depth (sslctx, 0);
220
221 /* debug massage about verify mode */
222 snprintf (tmpstr, TMPSTRSIZE, "# Connecting with verify depth %d in mode %d", SSL_CTX_get_verify_depth (sslctx), SSL_CTX_get_verify_mode (sslctx));
223 writecf (FS_DBG,tmpstr);
224
225 /* generate new SSL connection object and associate
226 * filedescriptor of server connection */
227 sslconn = SSL_new (sslctx);
228 SSL_set_fd (sslconn, serverfd);
229
230 /* try SSL handshake */
231 if (SSL_connect (sslconn) <= 0)
232 {
233 /* no SSL connection possible */
234 snprintf (tmpstr, TMPSTRSIZE, "!! SSL Connect fails: %s", ERR_error_string (ERR_get_error (), NULL));
235 writecf (FS_ERR,tmpstr);
236 return 0;
237 }
238 95
239 /* debug message about used symmetric cipher */ 96 SSL_library_init ();
240 snprintf (tmpstr, TMPSTRSIZE, "# SSL connection uses %s cipher (%d bits)", SSL_get_cipher_name (sslconn), SSL_get_cipher_bits (sslconn, NULL)); 97 SSL_load_error_strings();
241 writecf (FS_DBG,tmpstr);
242
243 /* if we can't get the servers certificate something is damn wrong */
244 if (!(sslserv = SSL_get_peer_certificate (sslconn)))
245 return 0;
246
247 /* ugly dump of server certificate */
248 /* TODO: make this happen elsewhere, and preferably only when user wants
249 * or a new key needs to be added to the known_hosts */
250 writecf (FS_DBG,"# SSL Server information:");
251 /* copy X.509 to local buffer */
252 strncpy (subjbuf, sslserv->name, 256);
253 /* split a subject line and print the fullname/value pairs */
254 subjv = &subjbuf[1];
255 while (subjv)
256 {
257 subjc = strchr (subjv, '=');
258 subjc[0] = '\0';
259 subjc++;
260 /* yeah, ugly */
261 if (!strcasecmp ("C", subjv))
262 {
263 subjn = "Country ";
264 }
265 else if (!strcasecmp ("ST", subjv))
266 {
267 subjn = "State ";
268 }
269 else if (!strcasecmp ("L", subjv))
270 {
271 subjn = "Location ";
272 }
273 else if (!strcasecmp ("O", subjv))
274 {
275 subjn = "Organization";
276 }
277 else if (!strcasecmp ("OU", subjv))
278 {
279 subjn = "Organiz.unit";
280 }
281 else if (!strcasecmp ("CN", subjv))
282 {
283 subjn = "Common name ";
284 subjh = subjc;
285 }
286 else if (!strcasecmp ("Email", subjv))
287 {
288 subjn = "Emailaddress";
289 }
290 else
291 {
292 subjn = "UNKNOWN ";
293 }
294 subjv = strchr (subjc, '/');
295 if (subjv)
296 {
297 subjv[0] = '\0';
298 subjv++;
299 }
300 /* print value pair */
301 snprintf (tmpstr, TMPSTRSIZE, "# %s: %s", subjn, subjc);
302 writecf (FS_DBG,tmpstr);
303 }
304 98
305 /* check if verifying the server's certificate yields any errors */ 99 vc_init_x509store(&vc_store);
306 verify = SSL_get_verify_result (sslconn);
307 if (verify)
308 {
309 /* it does - yield a warning to the user */
310 snprintf (tmpstr, TMPSTRSIZE, "!! Certificate verification fails: %s", X509_verify_cert_error_string (verify));
311 writecf (FS_ERR,tmpstr);
312 }
313 100
314 /* check if servers name in certificate matches the hostname we connected to */ 101 vc_x509store_setflags(&vc_store, VC_X509S_SSL_VERIFY_PEER);
315 if (subjh) 102 /* get name of certificate file */
316 if (strcasecmp (server, subjh)) 103 certfile = getstroption (CF_CERTFILE);
317 { 104
318 /* it doesn't - yield a warning and show difference */ 105 /* do we have a certificate file? */
319 writecf (FS_ERR,"!! Server name does not match name in presented certificate!"); 106 if (certfile) {
320 snprintf (tmpstr, TMPSTRSIZE, "!! '%s' != '%s'", server, subjh); 107 /* does the filename start with a tilde? expand it! */
321 writecf (FS_ERR,tmpstr); 108 if (certfile[0] == '~')
322 } 109 tildex = tilde_expand (certfile);
323
324 /* try to get the servers public key */
325 certpubkey = X509_get_pubkey (sslserv);
326 if (certpubkey == NULL)
327 {
328 /* huh, we can't? */
329 writecf (FS_ERR,"!! Can't get the public key associated to the certificate");
330 }
331 /* check what type of key we've got here */
332 else if (certpubkey->type == EVP_PKEY_RSA)
333 {
334 /* RSA key, convert bignum values from OpenSSL's structures to
335 * something readable */
336 sslpubkey = BN_bn2hex (certpubkey->pkey.rsa->n);
337 sslpubkeybits = BN_num_bits (certpubkey->pkey.rsa->n);
338 /* dump keylength and hexstring of servers key to user */
339 snprintf (tmpstr, TMPSTRSIZE, "# RSA public key%s: %d %s", (certpubkey->pkey.rsa-> d) ? " (private key available)" : "", sslpubkeybits, sslpubkey);
340 writecf (FS_DBG,tmpstr);
341 /* TODO: known_hosts check here ... */
342 }
343 else if (certpubkey->type == EVP_PKEY_DSA)
344 {
345 /* Can't handle (and didn't encounter) DSA keys */
346 writecf (FS_ERR,"# DSA Public Key (output currently not supported)");
347 /* TODO: known_hosts check here ... */
348 }
349 else 110 else
350 { 111 tildex = certfile;
351 writecf (FS_ERR,"# Public Key type not supported"); 112
352 /* TODO: fail known_hosts check ... */ 113 if (usingcert) {
353 } 114
354 } 115 vc_x509store_setflags(&vc_store, VC_X509S_USE_CERTIFICATE);
116 vc_x509store_setcertfile(&vc_store, tildex);
117
118 /* get name of key file */
119 keyfile = getstroption (CF_KEYFILE);
120
121 /* if we don't have a key file, the key may be in the cert file */
122 if (!keyfile)
123 keyfile = certfile;
355 124
356 /* if we didn't fail until now, we've got a connection. */ 125 /* does the filename start with a tilde? expand it! */
357 return 1; 126 if (keyfile[0] == '~')
127 tildex = tilde_expand (keyfile);
128 else
129 tildex = keyfile;
130
131 vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt);
132 vc_x509store_setkeyfile(&vc_store, tildex);
133
134 /* check if OpenSSL thinks key & cert belong together */
135 /* result = SSL_CTX_check_private_key (sslctx); THS TODO (->
136 * vchat-ssl.c) */
137 }
138 }
139
140 usessl = getintoption(CF_USESSL);
141 vc_x509store_setignssl(&vc_store, getintoption(CF_IGNSSL));
142
143 sslconn = vc_connect(server, getportnum(port), usessl, &vc_store, &sslctx);
144
145 if(sslconn == NULL) {
146 exitui();
147 exit(-1);
148 }
149
150 serverfd = BIO_get_fd(sslconn, 0);
151
152 /* inform user */
153 snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, getportnum(port));
154 writechan (tmpstr);
155
156 /* dump x509 details here... TODO */
157 writecf (FS_DBG,"# SSL Server information: TODO :)");
158
159 /* if we didn't fail until now, we've got a connection. */
160 return 1;
358} 161}
359 162
360/* disconnect from server */ 163/* disconnect from server */
@@ -1021,11 +824,8 @@ networkinput (void)
1021 char *ltmp = buf; 824 char *ltmp = buf;
1022 buf[BUFSIZE-1] = '\0'; /* sanity stop */ 825 buf[BUFSIZE-1] = '\0'; /* sanity stop */
1023 826
1024 /* check if we use ssl or if we don't and receive data at offset */ 827 /* receive data at offset */
1025 if (!usessl) 828 bytes = BIO_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff);
1026 bytes = recv (serverfd, &buf[bufoff], BUFSIZE-1 - bufoff, 0);
1027 else
1028 bytes = SSL_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff);
1029 829
1030 /* no bytes transferred? raise error message, bail out */ 830 /* no bytes transferred? raise error message, bail out */
1031 if (bytes < 0) 831 if (bytes < 0)
@@ -1093,23 +893,11 @@ networkoutput (char *msg)
1093 fprintf (stderr, ">| %s\n", msg); 893 fprintf (stderr, ">| %s\n", msg);
1094#endif 894#endif
1095 895
1096 /* TODO: err .. rework this (blocking) code */ 896 /* send data to server */
897 if (BIO_write (sslconn, msg, strlen (msg)) != strlen (msg))
898 writecf (FS_ERR,"Message sending fuzzy.");
1097 899
1098 /* check if we use ssl or if we don't and send data to server */ 900 /* send line termination to server */
1099 if (!usessl) { 901 if (BIO_write (sslconn, "\r\n", 2) != 2)
1100 if (send (serverfd, msg, strlen (msg), 0) != strlen (msg)) { 902 writecf (FS_ERR,"Message sending fuzzy.");
1101 writecf (FS_ERR,"Message sending fuzzy.");
1102 }
1103 } else if (SSL_write (sslconn, msg, strlen (msg)) != strlen (msg)) {
1104 writecf (FS_ERR,"Message sending fuzzy.");
1105 }
1106
1107 /* check if we use ssl or if we don't and send line termination to server */
1108 if (!usessl) {
1109 if (send (serverfd, "\r\n", 2, 0) != 2) {
1110 writecf (FS_ERR,"Message sending fuzzy!");
1111 }
1112 } else if (SSL_write (sslconn, "\r\n", 2) != 2) {
1113 writecf (FS_ERR,"Message sending fuzzy.");
1114 }
1115} 903}
diff --git a/vchat-ssl.c b/vchat-ssl.c
new file mode 100755
index 0000000..5d68309
--- /dev/null
+++ b/vchat-ssl.c
@@ -0,0 +1,467 @@
1/*
2 * vchat-client - alpha version
3 * vchat-ssl.c - handling of SSL connection and X509 certificate
4 * verification
5 *
6 * Copyright (C) 2007 Thorsten Schroeder <ths@berlin.ccc.de>
7 *
8 * This program is free software. It can be redistributed and/or modified,
9 * provided that this copyright notice is kept intact. This program is
10 * distributed in the hope that it will be useful, but without any warranty;
11 * without even the implied warranty of merchantability or fitness for a
12 * particular purpose. In no event shall the copyright holder be liable for
13 * any direct, indirect, incidental or special damages arising in any way out
14 * of the use of this software.
15 *
16 */
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include <openssl/err.h>
23#include <openssl/ssl.h>
24#include <openssl/bio.h>
25#include <openssl/evp.h>
26#include <openssl/x509.h>
27#include <openssl/x509v3.h>
28#include <openssl/conf.h>
29
30#include "vchat.h"
31#include "vchat-ssl.h"
32
33char *vchat_ssl_version = "$Id$";
34
35static int ignore_ssl;
36
37#define VC_CTX_ERR_EXIT(se, cx) do { \
38 snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", \
39 ERR_error_string (ERR_get_error (), NULL)); \
40 writecf(FS_ERR, tmpstr); \
41 if(se) X509_STORE_free(se); \
42 if(cx) SSL_CTX_free(cx); \
43 return(0); \
44 } while(0)
45
46#define VC_SETCERT_ERR_EXIT(se, cx, err) do { \
47 snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", err); \
48 writecf(FS_ERR, tmpstr); \
49 if(se) X509_STORE_free(se); \
50 if(cx) SSL_CTX_free(cx); \
51 return(NULL); \
52 } while(0)
53
54SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store )
55{
56 int i = 0;
57 int n = 0;
58 int flags = 0;
59 int r = 0;
60 SSL_CTX *ctx = NULL;
61 X509_STORE *store = NULL;
62 vc_x509verify_cb_t verify_callback = NULL;
63
64 if( !(ctx = SSL_CTX_new(SSLv3_method())) )
65 VC_CTX_ERR_EXIT(store, ctx);
66
67 if( !(store = vc_x509store_create(vc_store)) )
68 VC_CTX_ERR_EXIT(store, ctx);
69
70 SSL_CTX_set_cert_store(ctx, store);
71 store = NULL;
72 SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2);
73 SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
74
75 SSL_CTX_set_verify_depth (ctx, 2);
76
77 if( !(verify_callback = vc_store->callback) )
78 verify_callback = vc_verify_callback;
79
80 if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) {
81 writecf(FS_DBG, tmpstr);
82 flags = SSL_VERIFY_NONE;
83 }
84 else {
85 if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER)
86 flags |= SSL_VERIFY_PEER;
87 if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
88 flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
89 if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE)
90 flags |= SSL_VERIFY_CLIENT_ONCE;
91 }
92
93 SSL_CTX_set_verify(ctx, flags, verify_callback);
94
95 if(vc_store->flags & VC_X509S_USE_CERTIFICATE) {
96 if(vc_store->use_certfile)
97 SSL_CTX_use_certificate_chain_file(ctx, vc_store->use_certfile);
98 else {
99 SSL_CTX_use_certificate(ctx,
100 sk_X509_value(vc_store->use_certs, 0));
101 for(i=0,n=sk_X509_num(vc_store->use_certs); i<n; i++)
102 SSL_CTX_add_extra_chain_cert(ctx,
103 sk_X509_value(vc_store->use_certs, i));
104 }
105
106 SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback);
107
108 if(vc_store->use_keyfile) {
109 r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->use_keyfile,
110 SSL_FILETYPE_PEM);
111 } else if(vc_store->use_key)
112 r=SSL_CTX_use_PrivateKey(ctx, vc_store->use_key);
113
114 if(r!=1)
115 VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed");
116
117 }
118
119 SSL_CTX_set_app_data(ctx, vc_store);
120 return(ctx);
121}
122
123#define VC_CONNSSL_ERR_EXIT(_cx, cx, cn) do { \
124 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \
125 ERR_error_string (ERR_get_error (), NULL)); \
126 writecf(FS_ERR, tmpstr); \
127 if(cn) BIO_free_all(cn); \
128 if(*cx) SSL_CTX_free(*cx); \
129 if(_cx) *cx = 0; \
130 return(0); \
131 } while(0)
132
133BIO * vc_connect_ssl(char *host, int port, vc_x509store_t *vc_store,
134 SSL_CTX **ctx)
135{
136 BIO *conn = NULL;
137 int _ctx = 0;
138
139 if(*ctx) {
140 CRYPTO_add( &((*ctx)->references), 1, CRYPTO_LOCK_SSL_CTX );
141 if( vc_store && vc_store != SSL_CTX_get_app_data(*ctx) ) {
142 SSL_CTX_set_cert_store(*ctx, vc_x509store_create(vc_store));
143 SSL_CTX_set_app_data(*ctx, vc_store);
144 }
145 } else {
146 *ctx = vc_create_sslctx(vc_store);
147 _ctx = 1;
148 }
149
150 if( !(conn = BIO_new_ssl_connect(*ctx)) )
151 VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn);
152
153 BIO_set_conn_hostname(conn, host);
154 BIO_set_conn_int_port(conn, &port);
155
156 fflush(stdout);
157 if(BIO_do_connect(conn) <= 0)
158 VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn);
159
160 if(_ctx)
161 SSL_CTX_free(*ctx);
162
163 return(conn);
164}
165
166#define VC_CONN_ERR_EXIT(cn) do { \
167 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \
168 ERR_error_string(ERR_get_error(), NULL)); \
169 if(ERR_get_error()) \
170 writecf(FS_ERR, tmpstr); \
171 if(cn) BIO_free_all(cn); \
172 return(NULL); \
173 } while(0)
174
175#define VC_VERIFICATION_ERR_EXIT(cn, err) do { \
176 snprintf(tmpstr, TMPSTRSIZE, \
177 "[SSL ERROR] certificate verify failed: %s", err); \
178 writecf(FS_ERR, tmpstr); \
179 if(cn && !ignore_ssl) { BIO_free_all(cn); return(NULL); } \
180 } while(0)
181
182BIO * vc_connect( char *host, int port, int use_ssl,
183 vc_x509store_t *vc_store, SSL_CTX **ctx)
184{
185 BIO *conn = NULL;
186 SSL *ssl = NULL;
187
188 if(use_ssl) {
189 if( !(conn = vc_connect_ssl(host, port, vc_store, ctx)) )
190 VC_CONN_ERR_EXIT(conn);
191
192
193 BIO_get_ssl(conn, &ssl);
194 if(!vc_verify_cert_hostname(SSL_get_peer_certificate(ssl), host))
195 VC_VERIFICATION_ERR_EXIT(conn, "Hostname does not match!");
196
197 return(conn);
198 }
199
200 *ctx = 0;
201
202 if( !(conn = BIO_new_connect(host)) )
203 VC_CONN_ERR_EXIT(conn);
204
205 BIO_set_conn_int_port(conn, &port);
206
207 if(BIO_do_connect(conn) <= 0)
208 VC_CONN_ERR_EXIT(conn);
209
210 return(conn);
211}
212
213int vc_verify_cert_hostname(X509 *cert, char *host)
214{
215
216 int i = 0;
217 int j = 0;
218 int n = 0;
219 int extcount = 0;
220 int ok = 0;
221
222 X509_NAME *subj = NULL;
223 const char *extstr = NULL;
224 CONF_VALUE *nval = NULL;
225 unsigned char *data = NULL;
226 X509_EXTENSION *ext = NULL;
227 X509V3_EXT_METHOD *meth = NULL;
228 STACK_OF(CONF_VALUE) *val = NULL;
229
230 char name[256];
231 memset(&name, 0, sizeof(name));
232
233 if((extcount = X509_get_ext_count(cert)) > 0) {
234
235 for(i=0; !ok && i < extcount; i++) {
236
237 meth = NULL;
238
239 ext = X509_get_ext(cert, i);
240 extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
241
242 if(!strcasecmp(extstr, "subjectAltName")) {
243
244 if( !(meth = X509V3_EXT_get(ext)) )
245 break;
246
247 if( !(meth->d2i) )
248 break;
249
250 data = ext->value->data;
251
252 val = meth->i2v(meth, meth->d2i(0, &data, ext->value->length), 0);
253 for( j=0, n=sk_CONF_VALUE_num(val); j<n; j++ ) {
254 nval = sk_CONF_VALUE_value(val, j);
255 if( !strcasecmp(nval->name, "DNS") &&
256 !strcasecmp(nval->value, host) ) {
257 ok = 1;
258 break;
259 }
260 }
261 }
262 }
263 }
264
265 if( !ok && (subj = X509_get_subject_name(cert)) &&
266 X509_NAME_get_text_by_NID(subj, NID_commonName,
267 name, sizeof(name)) > 0 ) {
268 name[sizeof(name)-1] = '\0';
269 if(!strcasecmp(name, host))
270 ok = 1;
271 }
272
273 //printf("[*] vc_verify_cert_hostname() return: %d\n", ok);
274 return(ok);
275}
276
277int vc_verify_cert(X509 *cert, vc_x509store_t *vc_store)
278{
279 int result = -1;
280 X509_STORE *store = NULL;
281 X509_STORE_CTX *ctx = NULL;
282
283 if( !(store = vc_x509store_create(vc_store)) )
284 return(result);
285
286 if( (ctx = X509_STORE_CTX_new()) != 0 ) {
287 if(X509_STORE_CTX_init(ctx, store, cert, 0) == 1)
288 result = (X509_verify_cert(ctx) == 1);
289 X509_STORE_CTX_free(ctx);
290 }
291
292 X509_STORE_free(store);
293 return(result);
294}
295
296#define VC_STORE_ERR_EXIT(s) do { \
297 fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \
298 if(s) X509_STORE_free(s); \
299 return(0); \
300 } while(0)
301
302X509_STORE *vc_x509store_create(vc_x509store_t *vc_store)
303{
304 int i = 0;
305 int n = 0;
306 X509_STORE *store = NULL;
307 X509_LOOKUP *lookup = NULL;
308
309 store = X509_STORE_new();
310
311 if(vc_store->callback)
312 X509_STORE_set_verify_cb_func(store, vc_store->callback);
313 else
314 X509_STORE_set_verify_cb_func(store, vc_verify_callback);
315
316 if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) )
317 VC_STORE_ERR_EXIT(store);
318
319 if(!vc_store->cafile) {
320 if( !(vc_store->flags & VC_X509S_NODEF_CAFILE) )
321 X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT);
322 } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile,
323 X509_FILETYPE_PEM) )
324 VC_STORE_ERR_EXIT(store);
325
326 if(vc_store->crlfile) {
327 if( !X509_load_crl_file(lookup, vc_store->crlfile,
328 X509_FILETYPE_PEM) )
329 VC_STORE_ERR_EXIT(store);
330
331 X509_STORE_set_flags( store,
332 X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL );
333 }
334
335 if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) )
336 VC_STORE_ERR_EXIT(store);
337
338 if( !vc_store->capath ) {
339 if( !(vc_store->flags & VC_X509S_NODEF_CAPATH) )
340 X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT);
341 } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath,
342 X509_FILETYPE_PEM) )
343 VC_STORE_ERR_EXIT(store);
344
345 for( i=0, n=sk_X509_num(vc_store->certs); i<n; i++)
346 if( !X509_STORE_add_cert(store, sk_X509_value(vc_store->certs, i)) )
347 VC_STORE_ERR_EXIT(store);
348
349 for( i=0, n=sk_X509_CRL_num(vc_store->crls); i<n; i++)
350 if( !X509_STORE_add_crl(store,
351 sk_X509_CRL_value(vc_store->crls, i)) )
352 VC_STORE_ERR_EXIT(store);
353
354 return(store);
355}
356
357int vc_verify_callback(int ok, X509_STORE_CTX *store)
358{
359 if(!ok) {
360 /* XXX handle action/abort */
361 if(!ignore_ssl)
362 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s",
363 X509_verify_cert_error_string(store->error));
364 else
365 snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s (ignored)",
366 X509_verify_cert_error_string(store->error));
367
368 writecf(FS_ERR, tmpstr);
369 ok = ignore_ssl;
370 }
371 return(ok);
372}
373
374void vc_x509store_setflags(vc_x509store_t *store, int flags)
375{
376 store->flags |= flags;
377}
378
379void vc_x509store_setignssl(vc_x509store_t *store, int ignore)
380{
381 store->ignore_ssl |= ignore;
382 ignore_ssl = ignore;
383}
384
385void vc_x509store_clearflags(vc_x509store_t *store, int flags)
386{
387 store->flags &= ~flags;
388}
389
390void vc_x509store_setcb(vc_x509store_t *store,
391 vc_x509verify_cb_t callback)
392{
393 store->callback = callback;
394}
395
396void vc_x509store_set_pkeycb(vc_x509store_t *store,
397 vc_askpass_cb_t callback)
398{
399 store->askpass_callback = callback;
400}
401
402void vc_x509store_addcert(vc_x509store_t *store, X509 *cert)
403{
404 sk_X509_push(store->certs, cert);
405}
406
407void vc_x509store_setcafile(vc_x509store_t *store, char *file)
408{
409 if( store->cafile) free(store->cafile);
410 store->cafile = ( file ? strdup(file) : 0 );
411}
412
413void vc_x509store_setcapath(vc_x509store_t *store, char *path)
414{
415 if( store->capath) free(store->capath);
416 store->capath = ( path ? strdup(path) : 0 );
417}
418
419void vc_x509store_setcrlfile(vc_x509store_t *store, char *file)
420{
421 if( store->crlfile) free(store->crlfile);
422 store->crlfile = ( file ? strdup(file) : 0 );
423}
424
425void vc_x509store_setkeyfile(vc_x509store_t *store, char *file)
426{
427 if( store->use_keyfile) free(store->use_keyfile);
428 store->use_keyfile = ( file ? strdup(file) : 0 );
429}
430
431void vc_x509store_setcertfile(vc_x509store_t *store, char *file)
432{
433 if( store->use_certfile) free(store->use_certfile);
434 store->use_certfile = ( file ? strdup(file) : 0 );
435}
436
437
438void vc_init_x509store(vc_x509store_t *s)
439{
440 s->cafile = NULL;
441 s->capath = NULL;
442 s->crlfile = NULL;
443 s->callback = NULL;
444 s->askpass_callback = NULL;
445 s->certs = sk_X509_new_null();
446 s->crls = sk_X509_CRL_new_null();
447 s->use_certfile = NULL;
448 s->use_certs = sk_X509_new_null();
449 s->use_keyfile = NULL;
450 s->use_key = NULL;
451 s->flags = 0;
452 s->ignore_ssl = 0;
453}
454
455void vc_cleanup_x509store(vc_x509store_t *s)
456{
457 if(s->cafile) free(s->cafile);
458 if(s->capath) free(s->capath);
459 if(s->crlfile) free(s->crlfile);
460 if(s->use_certfile) free(s->use_certfile);
461 if(s->use_keyfile) free(s->use_keyfile);
462 if(s->use_key) free(s->use_key);
463 sk_X509_free(s->certs);
464 sk_X509_free(s->crls);
465 sk_X509_free(s->use_certs);
466 s->ignore_ssl = 0;
467}
diff --git a/vchat-ssl.h b/vchat-ssl.h
new file mode 100755
index 0000000..58e9dec
--- /dev/null
+++ b/vchat-ssl.h
@@ -0,0 +1,53 @@
1
2/* types */
3
4typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *);
5typedef int (*vc_askpass_cb_t)(char *, int, int, void *);
6typedef struct {
7 char *cafile;
8 char *capath;
9 char *crlfile;
10 vc_x509verify_cb_t callback;
11 vc_askpass_cb_t askpass_callback;
12 STACK_OF(X509) *certs;
13 STACK_OF(X509_CRL) *crls;
14 char *use_certfile;
15 STACK_OF(X509) *use_certs;
16 char *use_keyfile;
17 EVP_PKEY *use_key;
18 int flags;
19 int ignore_ssl;
20} vc_x509store_t;
21
22/* prototypes */
23
24BIO * vc_connect(char *, int , int, vc_x509store_t *, SSL_CTX **);
25BIO * vc_connect_ssl(char *, int, vc_x509store_t *, SSL_CTX **);
26SSL_CTX * vc_create_sslctx( vc_x509store_t *);
27void vc_init_x509store(vc_x509store_t *);
28void vc_cleanup_x509store(vc_x509store_t *);
29void vc_x509store_setcafile(vc_x509store_t *, char *);
30void vc_x509store_setcapath(vc_x509store_t *, char *);
31void vc_x509store_setcrlfile(vc_x509store_t *, char *);
32void vc_x509store_setkeyfile(vc_x509store_t *, char *);
33void vc_x509store_setcertfile(vc_x509store_t *, char *);
34void vc_x509store_addcert(vc_x509store_t *, X509 *);
35void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t);
36void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t);
37void vc_x509store_setflags(vc_x509store_t *, int);
38void vc_x509store_setignssl(vc_x509store_t *, int);
39void vc_x509store_clearflags(vc_x509store_t *, int);
40int vc_verify_cert(X509 *, vc_x509store_t *);
41int vc_verify_cert_hostname(X509 *, char *);
42int vc_verify_callback(int, X509_STORE_CTX *);
43X509_STORE * vc_x509store_create(vc_x509store_t *);
44
45#define VC_X509S_NODEF_CAFILE 0x01
46#define VC_X509S_NODEF_CAPATH 0x02
47#define VC_X509S_USE_CERTIFICATE 0x04
48#define VC_X509S_SSL_VERIFY_NONE 0x10
49#define VC_X509S_SSL_VERIFY_PEER 0x20
50#define VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40
51#define VC_X509S_SSL_VERIFY_CLIENT_ONCE 0x80
52#define VC_X509S_SSL_VERIFY_MASK 0xF0
53
diff --git a/vchat-ui.c b/vchat-ui.c
index 51e33d5..ce0260c 100755
--- a/vchat-ui.c
+++ b/vchat-ui.c
@@ -24,6 +24,7 @@
24#include <termios.h> 24#include <termios.h>
25#include <sys/ioctl.h> 25#include <sys/ioctl.h>
26#include <time.h> 26#include <time.h>
27#include <string.h>
27#include <readline/readline.h> 28#include <readline/readline.h>
28#include <readline/history.h> 29#include <readline/history.h>
29#include <openssl/pem.h> 30#include <openssl/pem.h>
@@ -35,7 +36,7 @@ char *vchat_ui_version = "$Id$";
35 36
36/* externally used variables */ 37/* externally used variables */
37/* current string in topic window */ 38/* current string in topic window */
38char topicstr[TOPICSTRSIZE] = "[] VChat 0.16"; 39char topicstr[TOPICSTRSIZE] = "[] VChat 0.17";
39/* current string in console window */ 40/* current string in console window */
40char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands"; 41char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands";
41 42
diff --git a/vchat.h b/vchat.h
index fbc9a2e..83bae1f 100755
--- a/vchat.h
+++ b/vchat.h
@@ -37,7 +37,7 @@ typedef struct servermessage servermessage;
37typedef enum { CO_NIL, CO_STR, CO_INT } conftype; 37typedef enum { CO_NIL, CO_STR, CO_INT } conftype;
38typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, 38typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT,
39CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, CF_LOGINSCRIPT, 39CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, CF_LOGINSCRIPT,
40CF_USESSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, 40CF_USESSL, CF_IGNSSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME,
41CF_USETOPIC, CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, 41CF_USETOPIC, CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT,
42CF_ENCODING, CF_BELLPRIV } confopt; 42CF_ENCODING, CF_BELLPRIV } confopt;
43 43