Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117886142
imclient.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
53 KB
Referenced Files
None
Subscribers
None
imclient.c
View Options
/* imclient.c -- Streaming IMxP client library
*
* $Id: imclient.c,v 1.92 2008/01/17 13:25:31 murch Exp $
*
* Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*
*/
#include
<config.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<ctype.h>
#include
<string.h>
#include
<errno.h>
#ifdef HAVE_UNISTD_H
#include
<unistd.h>
#endif
#ifdef HAVE_STDARG_H
#include
<stdarg.h>
#else
#include
<varargs.h>
#endif
#include
<sys/types.h>
#include
<sys/socket.h>
#include
<netinet/in.h>
#include
<netdb.h>
#ifdef HAVE_SYS_SELECT_H
#include
<sys/select.h>
#endif
#include
<sasl/sasl.h>
#ifdef HAVE_SSL
#include
<openssl/lhash.h>
#include
<openssl/bn.h>
#include
<openssl/err.h>
#include
<openssl/pem.h>
#include
<openssl/x509.h>
#include
<openssl/ssl.h>
#endif
/* HAVE_SSL */
#include
"assert.h"
#include
"exitcodes.h"
#include
"xmalloc.h"
#include
"xstrlcpy.h"
#include
"xstrlcat.h"
#include
"imparse.h"
#include
"imclient.h"
#include
"nonblock.h"
#include
"util.h"
#include
"iptostring.h"
/* I/O buffer size */
#define IMCLIENT_BUFSIZE 4096
/* Command completion callback record */
struct
imclient_cmdcallback
{
struct
imclient_cmdcallback
*
next
;
unsigned
long
tag
;
/* Command tag # */
imclient_proc_t
*
proc
;
/* Callback function */
void
*
rock
;
/* Callback rock */
};
/* Untagged data callback record */
struct
imclient_callback
{
int
flags
;
/* Information about untagged data */
char
*
keyword
;
/* Untagged data protocol keyword */
imclient_proc_t
*
proc
;
/* Callback function */
void
*
rock
;
/* Callback rock */
};
struct
stringlist
{
char
*
str
;
struct
stringlist
*
next
;
};
/* Connection data */
struct
imclient
{
/* TCP stream */
int
fd
;
char
*
servername
;
int
flags
;
/* Data to be output to server */
char
outbuf
[
IMCLIENT_BUFSIZE
];
char
*
outptr
;
size_t
outleft
;
char
*
outstart
;
/* Replies being received from server */
char
*
replybuf
;
char
*
replystart
;
size_t
replyliteralleft
;
size_t
replylen
;
size_t
alloc_replybuf
;
/* Protection mechanism data */
/* struct sasl_client *mech;
sasl_encodefunc_t *encodefunc;
sasl_decodefunc_t *decodefunc;*/
void
*
state
;
int
maxplain
;
unsigned
long
gensym
;
/* Tag value for previous command */
unsigned
long
readytag
;
/* Tag of command waiting for ready response */
/* 0 if wait over or not pending */
char
*
readytxt
;
/* Text of ready response, NULL if got
tagged reply for command */
/* Command callbacks */
struct
imclient_cmdcallback
*
cmdcallback
;
/* Untagged data callbacks */
int
callback_num
;
int
callback_alloc
;
struct
imclient_callback
*
callback
;
struct
stringlist
*
interact_results
;
sasl_conn_t
*
saslconn
;
int
saslcompleted
;
#ifdef HAVE_SSL
SSL_CTX
*
tls_ctx
;
SSL
*
tls_conn
;
int
tls_on
;
/* wheather we are under a layer or not */
#endif
/* HAVE_SSL */
};
/*
* Syntactic class of a character
* 0 - literal, 1 - quoted-string, 2 - atom
*/
static
const
char
charclass
[
256
]
=
{
0
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
/* 00 - 0f */
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
/* 10 - 1f */
1
,
2
,
0
,
2
,
2
,
1
,
2
,
2
,
1
,
1
,
1
,
2
,
2
,
2
,
2
,
2
,
/* ' ' - '/' */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* '0' - '?' */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* '@' - 'O' */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
0
,
2
,
2
,
2
,
/* 'P' - '_' */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
/* '`' - 'o' */
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
2
,
1
,
2
,
2
,
2
,
2
,
/* 'p' - DEL */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 80 - 8f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* 90 - 9f */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* a0 - af */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* b0 - bf */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* c0 - cf */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* d0 - df */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* e0 - ef */
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
,
/* f0 - ff */
};
/* Free list of command callback records */
static
struct
imclient_cmdcallback
*
cmdcallback_freelist
;
/* Forward declarations */
void
imclient_write
(
struct
imclient
*
imclient
,
const
char
*
s
,
size_t
len
);
static
int
imclient_writeastring
P
((
struct
imclient
*
imclient
,
const
char
*
str
));
static
void
imclient_writebase64
P
((
struct
imclient
*
imclient
,
const
char
*
output
,
size_t
len
));
static
void
imclient_eof
P
((
struct
imclient
*
imclient
));
static
int
imclient_decodebase64
P
((
char
*
input
));
/* callbacks we support */
static
const
sasl_callback_t
callbacks
[]
=
{
{
SASL_CB_USER
,
NULL
,
NULL
},
{
SASL_CB_GETREALM
,
NULL
,
NULL
},
{
SASL_CB_AUTHNAME
,
NULL
,
NULL
},
{
SASL_CB_PASS
,
NULL
,
NULL
},
{
SASL_CB_LIST_END
,
NULL
,
NULL
}
};
/*
* Connect to server on 'host'. Optional 'port' specifies the service
* to use. On success, returns zero and fills in the pointer pointed
* to by 'imclient' with a newly allocated connection pointer. On
* failure, returns errno if a system call failed, -1 if the hostname
* was not found, or -2 if the service name was not found.
* use sasl callbacks 'cbs'
*/
int
imclient_connect
(
struct
imclient
**
imclient
,
const
char
*
host
,
const
char
*
port
,
sasl_callback_t
*
cbs
)
{
int
s
=
-1
;
struct
addrinfo
hints
,
*
res0
=
NULL
,
*
res
;
int
saslresult
;
static
int
didinit
;
assert
(
imclient
);
assert
(
host
);
if
(
!
port
)
port
=
"143"
;
memset
(
&
hints
,
0
,
sizeof
(
hints
));
hints
.
ai_family
=
PF_UNSPEC
;
hints
.
ai_socktype
=
SOCK_STREAM
;
hints
.
ai_flags
=
AI_CANONNAME
;
if
(
getaddrinfo
(
host
,
port
,
&
hints
,
&
res0
))
return
-1
;
for
(
res
=
res0
;
res
;
res
=
res
->
ai_next
)
{
s
=
socket
(
res
->
ai_family
,
res
->
ai_socktype
,
res
->
ai_protocol
);
if
(
s
<
0
)
continue
;
if
(
connect
(
s
,
res
->
ai_addr
,
res
->
ai_addrlen
)
>=
0
)
break
;
close
(
s
);
s
=
-1
;
}
if
(
s
<
0
)
return
errno
;
/* nonblock(s, 1); */
*
imclient
=
(
struct
imclient
*
)
xzmalloc
(
sizeof
(
struct
imclient
));
(
*
imclient
)
->
fd
=
s
;
(
*
imclient
)
->
saslconn
=
NULL
;
(
*
imclient
)
->
saslcompleted
=
0
;
(
*
imclient
)
->
servername
=
xstrdup
(
res0
->
ai_canonname
?
res0
->
ai_canonname
:
host
);
freeaddrinfo
(
res0
);
(
*
imclient
)
->
outptr
=
(
*
imclient
)
->
outstart
=
(
*
imclient
)
->
outbuf
;
(
*
imclient
)
->
outleft
=
(
*
imclient
)
->
maxplain
=
sizeof
((
*
imclient
)
->
outbuf
);
(
*
imclient
)
->
interact_results
=
NULL
;
imclient_addcallback
(
*
imclient
,
""
,
0
,
(
imclient_proc_t
*
)
0
,
(
void
*
)
0
,
"OK"
,
CALLBACK_NOLITERAL
,
(
imclient_proc_t
*
)
0
,
(
void
*
)
0
,
"NO"
,
CALLBACK_NOLITERAL
,
(
imclient_proc_t
*
)
0
,
(
void
*
)
0
,
"BAD"
,
CALLBACK_NOLITERAL
,
(
imclient_proc_t
*
)
0
,
(
void
*
)
0
,
"BYE"
,
CALLBACK_NOLITERAL
,
(
imclient_proc_t
*
)
0
,
(
void
*
)
0
,
(
char
*
)
0
);
#ifdef HAVE_SSL
(
*
imclient
)
->
tls_ctx
=
NULL
;
(
*
imclient
)
->
tls_conn
=
NULL
;
(
*
imclient
)
->
tls_on
=
0
;
#endif
/* HAVE_SSL */
if
(
!
didinit
)
{
/* attempt to start sasl */
saslresult
=
sasl_client_init
(
NULL
);
if
(
saslresult
!=
SASL_OK
)
return
1
;
didinit
=
1
;
}
/* client new connection */
saslresult
=
sasl_client_new
(
"imap"
,
/* xxx ideally this should be configurable */
(
*
imclient
)
->
servername
,
NULL
,
NULL
,
cbs
?
cbs
:
callbacks
,
0
,
&
((
*
imclient
)
->
saslconn
));
if
(
saslresult
!=
SASL_OK
)
return
1
;
return
0
;
}
/*
* Close and free the connection 'imclient'
*/
void
imclient_close
(
struct
imclient
*
imclient
)
{
int
i
;
struct
stringlist
*
cur
,
*
cur_next
;
assert
(
imclient
);
imclient_eof
(
imclient
);
close
(
imclient
->
fd
);
free
(
imclient
->
servername
);
if
(
imclient
->
replybuf
)
free
(
imclient
->
replybuf
);
/* if (imclient->state) imclient->mech->free_state(imclient->state);*/
sasl_dispose
(
&
(
imclient
->
saslconn
));
for
(
i
=
0
;
i
<
imclient
->
callback_num
;
i
++
)
{
free
(
imclient
->
callback
[
i
].
keyword
);
}
if
(
imclient
->
callback
)
free
((
char
*
)
imclient
->
callback
);
for
(
cur
=
imclient
->
interact_results
;
cur
;
cur
=
cur_next
)
{
cur_next
=
cur
->
next
;
free
(
cur
->
str
);
free
(
cur
);
}
free
((
char
*
)
imclient
);
}
void
imclient_setflags
(
struct
imclient
*
imclient
,
int
flags
)
{
assert
(
imclient
);
imclient
->
flags
|=
flags
;
}
void
imclient_clearflags
(
struct
imclient
*
imclient
,
int
flags
)
{
assert
(
imclient
);
imclient
->
flags
&=
~
flags
;
}
char
*
imclient_servername
(
struct
imclient
*
imclient
)
{
assert
(
imclient
);
return
imclient
->
servername
;
}
#define CALLBACKGROW 5
/*
* Add untagged data callbacks to a connection.
* After the first argument 'imclient', there can be zero or more
* 4-tuples of 'keyword', 'flags', 'proc', 'rock', each adding or changing
* a single callback. The last 4-tuple is terminated by a single null pointer.
*
* Each 4-tuple adds or changes the callback for 'keyword'. 'flags' specifies
* information about the parsing of the untagged data. 'proc' and 'rock'
* specify the callback function and rock to invoke when the untagged data
* is received. 'proc' may be a null pointer, in which case no function is
* invoked.
*
* The callback function may not call the functions imclient_close(),
* imclient_send(), imclient_eof(), imclient_processoneevent(), or
* imclient_authenticate() on the connection. The callback function
* may scribble on the text of the untagged data.
*
*/
#ifdef __STDC__
void
imclient_addcallback
(
struct
imclient
*
imclient
,
...)
#else
void
imclient_addcallback
(
va_alist
)
va_dcl
#endif
{
va_list
pvar
;
char
*
keyword
;
int
flags
;
imclient_proc_t
*
proc
;
void
*
rock
;
int
i
;
#ifdef __STDC__
va_start
(
pvar
,
imclient
);
#else
struct
imclient
*
imclient
;
va_start
(
pvar
);
imclient
=
va_arg
(
pvar
,
struct
imclient
*
);
#endif
assert
(
imclient
);
while
((
keyword
=
va_arg
(
pvar
,
char
*
)))
{
flags
=
va_arg
(
pvar
,
int
);
proc
=
va_arg
(
pvar
,
imclient_proc_t
*
);
rock
=
va_arg
(
pvar
,
void
*
);
/* Search for existing callback matching keyword and flags */
for
(
i
=
0
;
i
<
imclient
->
callback_num
;
i
++
)
{
if
(
imclient
->
callback
[
i
].
flags
==
flags
&&
!
strcmp
(
imclient
->
callback
[
i
].
keyword
,
keyword
))
break
;
}
/* If not found, allocate new callback entry */
if
(
i
==
imclient
->
callback_num
)
{
if
(
imclient
->
callback_num
==
imclient
->
callback_alloc
)
{
imclient
->
callback_alloc
+=
CALLBACKGROW
;
imclient
->
callback
=
(
struct
imclient_callback
*
)
xrealloc
((
char
*
)
imclient
->
callback
,
imclient
->
callback_alloc
*
sizeof
(
struct
imclient_callback
));
}
imclient
->
callback_num
++
;
imclient
->
callback
[
i
].
keyword
=
xstrdup
(
keyword
);
imclient
->
callback
[
i
].
flags
=
flags
;
}
imclient
->
callback
[
i
].
proc
=
proc
;
imclient
->
callback
[
i
].
rock
=
rock
;
}
va_end
(
pvar
);
}
/*
* Send a new command on the connection 'imclient'.
*
* 'finishproc' and 'finishrock' are the function and rock called when
* the command completes. 'functionproc' may be a null pointer, in
* which case no callback is made. The callback function may not call
* the functions imclient_close(), imclient_send(), imclient_eof(),
* imclient_processoneevent(), or imclient_authenticate() on the
* connection. The callback function is guaranteed to be invoked, the
* special result type "EOF" is used in the case where the connection
* dies before a result is received from the server.
*
* 'fmt' is a printf-like specification of the command. It must not
* include the tag--that is automatically added by imclient_send().
* The defined %-sequences are as follows:
*
* %% -- %
* %a -- atom
* %s -- astring (will be quoted or literalized as needed)
* %d -- decimal
* %u -- unsigned decimal
* %v -- #astring (arg is an null-terminated array of (char *)
* which are written as space separated astrings)
* %B -- (internal use only) base64-encoded data at end of command line
*/
#ifdef __STDC__
void
imclient_send
(
struct
imclient
*
imclient
,
void
(
*
finishproc
)(),
void
*
finishrock
,
const
char
*
fmt
,
...)
#else
void
imclient_send
(
va_alist
)
va_dcl
#endif
{
va_list
pvar
;
struct
imclient_cmdcallback
*
newcmdcallback
;
char
buf
[
30
];
char
*
percent
,
*
str
,
**
v
;
int
num
;
unsigned
unum
;
int
abortcommand
=
0
;
#ifdef __STDC__
va_start
(
pvar
,
fmt
);
#else
struct
imclient
*
imclient
;
imclient_proc_t
*
finishproc
;
void
*
finishrock
;
char
*
fmt
;
va_start
(
pvar
);
imclient
=
va_arg
(
pvar
,
struct
imclient
*
);
finishproc
=
va_arg
(
pvar
,
imclient_proc_t
*
);
finishrock
=
va_arg
(
pvar
,
void
*
);
fmt
=
va_arg
(
pvar
,
char
*
);
#endif
assert
(
imclient
);
imclient
->
gensym
++
;
if
(
imclient
->
gensym
<=
0
)
imclient
->
gensym
=
1
;
/*
* If there is a command completion callback, add it to the
* command callback list of the imclient struct.
*/
if
(
finishproc
)
{
if
(
cmdcallback_freelist
)
{
newcmdcallback
=
cmdcallback_freelist
;
cmdcallback_freelist
=
newcmdcallback
->
next
;
}
else
{
newcmdcallback
=
(
struct
imclient_cmdcallback
*
)
xmalloc
(
sizeof
(
struct
imclient_cmdcallback
));
}
newcmdcallback
->
next
=
imclient
->
cmdcallback
;
newcmdcallback
->
tag
=
imclient
->
gensym
;
newcmdcallback
->
proc
=
finishproc
;
newcmdcallback
->
rock
=
finishrock
;
imclient
->
cmdcallback
=
newcmdcallback
;
}
/* Write the tag */
snprintf
(
buf
,
sizeof
(
buf
),
"%lu "
,
imclient
->
gensym
);
imclient_write
(
imclient
,
buf
,
strlen
(
buf
));
/* Process the command format */
while
((
percent
=
strchr
(
fmt
,
'%'
)))
{
imclient_write
(
imclient
,
fmt
,
percent
-
fmt
);
switch
(
*++
percent
)
{
case
'%'
:
imclient_write
(
imclient
,
percent
,
1
);
break
;
case
'a'
:
str
=
va_arg
(
pvar
,
char
*
);
imclient_write
(
imclient
,
str
,
strlen
(
str
));
break
;
case
's'
:
str
=
va_arg
(
pvar
,
char
*
);
abortcommand
=
imclient_writeastring
(
imclient
,
str
);
if
(
abortcommand
)
goto
fail
;
break
;
case
'd'
:
num
=
va_arg
(
pvar
,
int
);
snprintf
(
buf
,
sizeof
(
buf
),
"%d"
,
num
);
imclient_write
(
imclient
,
buf
,
strlen
(
buf
));
break
;
case
'u'
:
unum
=
va_arg
(
pvar
,
unsigned
);
snprintf
(
buf
,
sizeof
(
buf
),
"%lu"
,
(
unsigned
long
)
unum
);
imclient_write
(
imclient
,
buf
,
strlen
(
buf
));
break
;
case
'v'
:
v
=
va_arg
(
pvar
,
char
**
);
for
(
num
=
0
;
v
[
num
];
num
++
)
{
if
(
num
)
imclient_write
(
imclient
,
" "
,
1
);
abortcommand
=
imclient_writeastring
(
imclient
,
v
[
num
]);
if
(
abortcommand
)
goto
fail
;
}
break
;
case
'B'
:
num
=
va_arg
(
pvar
,
int
);
str
=
va_arg
(
pvar
,
char
*
);
imclient_writebase64
(
imclient
,
str
,
num
);
/* KLUDGE ALERT: imclientwritebase64() spit out a CRLF
* so fake things up to prevent our spitting out a second CRLF.
*/
abortcommand
=
1
;
goto
fail
;
default
:
fatal
(
"internal error: invalid format specifier in imclient_send"
,
EC_SOFTWARE
);
}
fmt
=
percent
+
1
;
}
fail
:
va_end
(
pvar
);
if
(
!
abortcommand
)
{
imclient_write
(
imclient
,
fmt
,
strlen
(
fmt
));
imclient_write
(
imclient
,
"
\r\n
"
,
2
);
}
}
static
int
imclient_writeastring
(
struct
imclient
*
imclient
,
const
char
*
str
)
{
const
char
*
p
;
unsigned
len
=
0
;
int
class
=
2
;
char
buf
[
30
];
assert
(
imclient
);
assert
(
str
);
for
(
p
=
str
;
*
p
;
p
++
)
{
len
++
;
if
(
class
>
charclass
[(
unsigned
char
)
*
p
])
{
class
=
charclass
[(
unsigned
char
)
*
p
];
}
}
if
(
len
>=
1024
)
class
=
0
;
if
(
len
&&
class
==
2
)
{
/* Atom */
imclient_write
(
imclient
,
str
,
len
);
}
else
if
(
class
)
{
/* Quoted-string */
imclient_write
(
imclient
,
"
\"
"
,
1
);
imclient_write
(
imclient
,
str
,
len
);
imclient_write
(
imclient
,
"
\"
"
,
1
);
}
else
{
/* Literal */
if
(
imclient
->
flags
&
IMCLIENT_CONN_NONSYNCLITERAL
)
{
snprintf
(
buf
,
sizeof
(
buf
),
"{%u+}
\r\n
"
,
len
);
imclient_write
(
imclient
,
buf
,
strlen
(
buf
));
}
else
{
imclient
->
readytag
=
imclient
->
gensym
;
snprintf
(
buf
,
sizeof
(
buf
),
"{%u}
\r\n
"
,
len
);
imclient_write
(
imclient
,
buf
,
strlen
(
buf
));
while
(
imclient
->
readytag
)
{
imclient_processoneevent
(
imclient
);
}
if
(
!
imclient
->
readytxt
)
return
1
;
}
imclient_write
(
imclient
,
str
,
len
);
}
return
0
;
}
/*
* Write to the connection 'imclient' the data 's', of length 'len'
*/
void
imclient_write
(
struct
imclient
*
imclient
,
const
char
*
s
,
size_t
len
)
{
assert
(
imclient
);
assert
(
s
);
/* If no data pending for output, reset the buffer */
if
(
imclient
->
outptr
==
imclient
->
outstart
)
{
imclient
->
outstart
=
imclient
->
outptr
=
imclient
->
outbuf
;
imclient
->
outleft
=
imclient
->
maxplain
;
}
/* While we don't have room to buffer all the output */
while
(
len
>
imclient
->
outleft
)
{
/* Copy as much data as will fit in output buffer */
memcpy
(
imclient
->
outptr
,
s
,
imclient
->
outleft
);
imclient
->
outptr
+=
imclient
->
outleft
;
s
+=
imclient
->
outleft
;
len
-=
imclient
->
outleft
;
imclient
->
outleft
=
0
;
/* Process events until output buffer is flushed */
while
(
imclient
->
outptr
!=
imclient
->
outstart
)
{
imclient_processoneevent
(
imclient
);
}
/* Reset the buffer */
imclient
->
outstart
=
imclient
->
outptr
=
imclient
->
outbuf
;
imclient
->
outleft
=
imclient
->
maxplain
;
}
/* Copy remaining data to output buffer */
memcpy
(
imclient
->
outptr
,
s
,
len
);
imclient
->
outptr
+=
len
;
imclient
->
outleft
-=
len
;
}
/*
* On the connection 'imclient', handle the input 'buf' of size 'len'
* from the server. Invoke callbacks as appropriate.
*/
#define REPLYSLACK 80
/* When growing, allocate this extra slack */
#define REPLYSHRINK (4096+500)
/* If more than this free, shrink buffer */
static
void
imclient_input
(
struct
imclient
*
imclient
,
char
*
buf
,
int
len
)
{
unsigned
long
replytag
;
struct
imclient_reply
reply
;
char
*
endreply
;
char
*
p
;
size_t
parsed
;
size_t
literallen
;
size_t
keywordlen
;
int
keywordindex
;
struct
imclient_cmdcallback
**
cmdcb
,
*
cmdcbtemp
;
const
char
*
plainbuf
;
unsigned
plainlen
;
int
result
;
assert
(
imclient
);
assert
(
buf
);
if
(
imclient
->
saslcompleted
==
1
)
{
/* decrypt what we have */
if
((
result
=
sasl_decode
(
imclient
->
saslconn
,
buf
,
len
,
&
plainbuf
,
&
plainlen
))
!=
SASL_OK
)
{
(
void
)
shutdown
(
imclient
->
fd
,
0
);
}
if
(
plainlen
==
0
)
return
;
}
else
{
plainbuf
=
buf
;
plainlen
=
len
;
}
/* Ensure replybuf has enough space to take the input */
if
(
imclient
->
replylen
+
plainlen
>=
imclient
->
alloc_replybuf
)
{
/* If there is unused space at the front, move the plaintext there */
if
(
imclient
->
replystart
!=
imclient
->
replybuf
)
{
imclient
->
replylen
-=
imclient
->
replystart
-
imclient
->
replybuf
;
memmove
(
imclient
->
replybuf
,
imclient
->
replystart
,
imclient
->
replylen
);
imclient
->
replystart
=
imclient
->
replybuf
;
}
/* Shrink the reply buffer if it's too large */
if
(
imclient
->
replylen
+
plainlen
+
REPLYSHRINK
<
imclient
->
alloc_replybuf
)
{
imclient
->
alloc_replybuf
=
imclient
->
replylen
+
plainlen
+
REPLYSHRINK
;
imclient
->
replybuf
=
xrealloc
(
imclient
->
replybuf
,
imclient
->
alloc_replybuf
);
imclient
->
replystart
=
imclient
->
replybuf
;
}
/* If there still isn't enough room, grow the buffer */
if
(
imclient
->
replylen
+
plainlen
>=
imclient
->
alloc_replybuf
)
{
imclient
->
alloc_replybuf
=
imclient
->
replylen
+
plainlen
+
REPLYSLACK
;
imclient
->
replybuf
=
xrealloc
(
imclient
->
replybuf
,
imclient
->
alloc_replybuf
);
imclient
->
replystart
=
imclient
->
replybuf
;
}
}
/* Remember where new data starts */
parsed
=
imclient
->
replylen
;
/* Copy the data to the buffer and NUL-terminate it */
memcpy
(
imclient
->
replybuf
+
imclient
->
replylen
,
plainbuf
,
plainlen
);
imclient
->
replylen
+=
plainlen
;
imclient
->
replybuf
[
imclient
->
replylen
]
=
'\0'
;
/* Process the new data (of length 'plainlen') */
while
(
parsed
<
imclient
->
replylen
)
{
/* If we're reading a literal, skip over it. */
if
(
imclient
->
replyliteralleft
)
{
size_t
avail
;
avail
=
imclient
->
replylen
-
parsed
;
if
(
avail
>
imclient
->
replyliteralleft
)
{
parsed
+=
imclient
->
replyliteralleft
;
imclient
->
replyliteralleft
=
0
;
continue
;
}
else
{
parsed
+=
avail
;
imclient
->
replyliteralleft
-=
avail
;
return
;
}
}
/* Look for the end of the line and skip over to it. */
endreply
=
(
char
*
)
memchr
(
imclient
->
replybuf
+
parsed
,
'\n'
,
imclient
->
replylen
-
parsed
);
/* Don't have a complete line */
if
(
!
endreply
)
return
;
parsed
=
endreply
-
imclient
->
replybuf
+
1
;
/* parse tag */
p
=
imclient
->
replystart
;
if
(
*
p
==
'+'
&&
p
[
1
]
==
' '
)
{
/* Ready response */
if
(
imclient
->
readytag
)
{
imclient
->
readytag
=
0
;
imclient
->
readytxt
=
p
+
2
;
*
(
endreply
-1
)
=
'\0'
;
}
else
{
/* XXX Got junk from the server */
}
/* Start parsing the next reply */
imclient
->
replystart
=
endreply
+
1
;
continue
;
}
else
if
(
*
p
==
'*'
&&
p
[
1
]
==
' '
)
{
replytag
=
0
;
p
+=
2
;
}
else
{
replytag
=
0
;
while
(
isdigit
((
unsigned
char
)
*
p
))
{
replytag
=
replytag
*
10
+
*
p
++
-
'0'
;
}
if
(
*
p
++
!=
' '
)
{
/* XXX Got junk from the server */
/* Start parsing the next reply */
imclient
->
replystart
=
endreply
+
1
;
continue
;
}
}
/* parse num, if there */
if
(
replytag
==
0
&&
isdigit
((
unsigned
char
)
*
p
))
{
reply
.
msgno
=
0
;
while
(
isdigit
((
unsigned
char
)
*
p
))
{
reply
.
msgno
=
reply
.
msgno
*
10
+
*
p
++
-
'0'
;
}
if
(
*
p
++
!=
' '
)
{
/* XXX Got junk from the server */
/* Start parsing the next reply */
imclient
->
replystart
=
endreply
+
1
;
continue
;
}
}
else
{
reply
.
msgno
=
-1
;
}
/* parse keyword */
reply
.
keyword
=
p
;
while
(
*
p
&&
*
p
!=
' '
&&
*
p
!=
'\n'
)
p
++
;
keywordlen
=
p
-
reply
.
keyword
;
reply
.
text
=
p
+
1
;
if
(
*
p
==
'\n'
)
{
if
(
keywordlen
&&
p
[
-1
]
==
'\r'
)
{
keywordlen
--
;
reply
.
text
--
;
}
reply
.
text
--
;
}
/* Handle tagged replies */
if
(
replytag
!=
0
)
{
int
iscompletion
=
((
keywordlen
==
3
&&
reply
.
keyword
[
0
]
==
'B'
&&
reply
.
keyword
[
1
]
==
'A'
&&
reply
.
keyword
[
2
]
==
'D'
)
||
(
keywordlen
==
2
&&
((
reply
.
keyword
[
0
]
==
'O'
&&
reply
.
keyword
[
1
]
==
'K'
)
||
(
reply
.
keyword
[
0
]
==
'N'
&&
reply
.
keyword
[
1
]
==
'O'
))));
/* Scan back and see if the end of the line introduces a literal */
if
(
!
iscompletion
&&
endreply
>
imclient
->
replystart
+
2
&&
endreply
[
-1
]
==
'\r'
&&
endreply
[
-2
]
==
'}'
&&
isdigit
((
unsigned
char
)
endreply
[
-3
]))
{
p
=
endreply
-
4
;
while
(
p
>
imclient
->
replystart
&&
isdigit
((
unsigned
char
)
*
p
))
{
p
--
;
}
if
(
p
>
imclient
->
replystart
+
2
&&
*
p
==
'{'
&&
charclass
[(
unsigned
char
)
p
[
-1
]]
!=
2
)
{
/* Parse the size of the literal */
literallen
=
0
;
p
++
;
while
(
isdigit
((
unsigned
char
)
*
p
))
{
literallen
=
literallen
*
10
+
*
p
++
-
'0'
;
}
/* Do a continue to read literal & following line */
imclient
->
replyliteralleft
=
literallen
;
continue
;
}
}
/* Start parsing the next reply */
imclient
->
replystart
=
endreply
+
1
;
if
(
replytag
==
imclient
->
readytag
)
{
imclient
->
readytag
=
0
;
imclient
->
readytxt
=
0
;
}
cmdcb
=
&
imclient
->
cmdcallback
;
while
(
*
cmdcb
&&
(
*
cmdcb
)
->
tag
!=
replytag
)
{
cmdcb
=
&
(
*
cmdcb
)
->
next
;
}
if
((
cmdcbtemp
=
*
cmdcb
))
{
if
(
iscompletion
)
{
/* Move callback struct to the freelist */
*
cmdcb
=
cmdcbtemp
->
next
;
cmdcbtemp
->
next
=
cmdcallback_freelist
;
cmdcallback_freelist
=
cmdcbtemp
;
}
/* Do the callback */
endreply
[
-1
]
=
'\0'
;
reply
.
keyword
[
keywordlen
]
=
'\0'
;
(
*
cmdcbtemp
->
proc
)(
imclient
,
cmdcbtemp
->
rock
,
&
reply
);
}
continue
;
}
/* Must be an untagged reply, look up the keyword */
for
(
keywordindex
=
1
;
keywordindex
<
imclient
->
callback_num
;
keywordindex
++
)
{
if
(
imclient
->
callback
[
keywordindex
].
flags
&
CALLBACK_NUMBERED
)
{
if
(
reply
.
msgno
==
-1
)
continue
;
}
else
{
if
(
reply
.
msgno
!=
-1
)
continue
;
}
if
(
!
strncmp
(
imclient
->
callback
[
keywordindex
].
keyword
,
reply
.
keyword
,
keywordlen
)
&&
imclient
->
callback
[
keywordindex
].
keyword
[
keywordlen
]
==
'\0'
&&
imclient
->
callback
[
keywordindex
].
proc
)
break
;
}
/* Keyword index 0 is the default callback */
if
(
keywordindex
==
imclient
->
callback_num
)
keywordindex
=
0
;
/* Scan back and see if the end of the line introduces a literal */
if
(
!
(
imclient
->
callback
[
keywordindex
].
flags
&
CALLBACK_NOLITERAL
))
{
if
(
endreply
>
imclient
->
replystart
+
2
&&
endreply
[
-1
]
==
'\r'
&&
endreply
[
-2
]
==
'}'
&&
isdigit
((
unsigned
char
)
endreply
[
-3
]))
{
p
=
endreply
-
4
;
while
(
p
>
imclient
->
replystart
&&
isdigit
((
unsigned
char
)
*
p
))
{
p
--
;
}
if
(
p
>
imclient
->
replystart
+
2
&&
*
p
==
'{'
&&
charclass
[(
unsigned
char
)
p
[
-1
]]
!=
2
)
{
/* Parse the size of the literal */
literallen
=
0
;
p
++
;
while
(
isdigit
((
unsigned
char
)
*
p
))
{
literallen
=
literallen
*
10
+
*
p
++
-
'0'
;
}
/* Do a continue to read literal & following line */
imclient
->
replyliteralleft
=
literallen
;
continue
;
}
}
}
/* Do the callback, if the proc is non-null */
if
(
imclient
->
callback
[
keywordindex
].
proc
)
{
endreply
[
-1
]
=
'\0'
;
reply
.
keyword
[
keywordlen
]
=
'\0'
;
(
imclient
->
callback
[
keywordindex
].
proc
)
(
imclient
,
imclient
->
callback
[
keywordindex
].
rock
,
&
reply
);
}
/* Start parsing the next reply */
imclient
->
replystart
=
endreply
+
1
;
}
}
/*
* Received an EOF on the connection 'imclient'
* Issue appropriate callbacks.
*/
static
void
imclient_eof
(
struct
imclient
*
imclient
)
{
struct
imclient_cmdcallback
*
cmdcb
;
struct
imclient_reply
reply
;
assert
(
imclient
);
imclient
->
readytag
=
0
;
imclient
->
readytxt
=
0
;
for
(
cmdcb
=
imclient
->
cmdcallback
;
cmdcb
;
cmdcb
=
cmdcb
->
next
)
{
reply
.
keyword
=
"EOF"
;
reply
.
msgno
=
-1
;
reply
.
text
=
""
;
(
*
cmdcb
->
proc
)(
imclient
,
cmdcb
->
rock
,
&
reply
);
if
(
!
cmdcb
->
next
)
{
cmdcb
->
next
=
cmdcallback_freelist
;
cmdcallback_freelist
=
imclient
->
cmdcallback
;
break
;
}
}
imclient
->
cmdcallback
=
0
;
/* XXX make an untagged "EOF" callback? */
}
/*
* Get information for calling select
* 'fd' is filled in with file descriptor to select() for read
* 'wanttowrite' is filled in with nonzero value iff should
* select() for write as well.
*/
void
imclient_getselectinfo
(
struct
imclient
*
imclient
,
int
*
fd
,
int
*
wanttowrite
)
{
assert
(
imclient
);
assert
(
fd
);
assert
(
wanttowrite
);
*
fd
=
imclient
->
fd
;
*
wanttowrite
=
imclient
->
outptr
-
imclient
->
outstart
;
}
/*
* Process one input or output event on the connection 'imclient'.
*/
void
imclient_processoneevent
(
struct
imclient
*
imclient
)
{
char
buf
[
IMCLIENT_BUFSIZE
];
int
n
;
int
writelen
;
fd_set
rfds
,
wfds
;
FD_ZERO
(
&
rfds
);
FD_ZERO
(
&
wfds
);
assert
(
imclient
);
for
(;;)
{
writelen
=
imclient
->
outptr
-
imclient
->
outstart
;
if
((
imclient
->
saslcompleted
==
1
)
&&
(
writelen
>
0
))
{
unsigned
int
cryptlen
=
0
;
const
char
*
cryptptr
=
NULL
;
if
(
sasl_encode
(
imclient
->
saslconn
,
imclient
->
outstart
,
writelen
,
&
cryptptr
,
&
cryptlen
)
!=
SASL_OK
)
{
/* XXX encoding error */
n
=
0
;
}
#ifdef HAVE_SSL
if
(
imclient
->
tls_on
==
1
)
{
n
=
SSL_write
(
imclient
->
tls_conn
,
cryptptr
,
cryptlen
);
}
else
{
n
=
write
(
imclient
->
fd
,
cryptptr
,
cryptlen
);
}
#else
/* HAVE_SSL */
n
=
write
(
imclient
->
fd
,
cryptptr
,
cryptlen
);
#endif
/* HAVE_SSL */
if
(
n
>
0
)
{
imclient
->
outstart
+=
writelen
;
return
;
}
/* XXX Also EPIPE & the like? */
/* Make sure we select() for writing */
}
else
if
(
writelen
)
{
/* No protection mechanism, just write the plaintext */
#ifdef HAVE_SSL
if
(
imclient
->
tls_on
==
1
)
{
n
=
SSL_write
(
imclient
->
tls_conn
,
imclient
->
outstart
,
writelen
);
}
else
{
n
=
write
(
imclient
->
fd
,
imclient
->
outstart
,
writelen
);
}
#else
/* HAVE_SSL */
n
=
write
(
imclient
->
fd
,
imclient
->
outstart
,
writelen
);
#endif
/* HAVE_SSL */
if
(
n
>
0
)
{
imclient
->
outstart
+=
n
;
return
;
}
/* XXX Also EPIPE & the like? */
}
if
(
FD_ISSET
(
imclient
->
fd
,
&
rfds
))
{
#ifdef HAVE_SSL
/* just do a SSL read instead if we're under a tls layer */
if
(
imclient
->
tls_on
==
1
)
{
n
=
SSL_read
(
imclient
->
tls_conn
,
buf
,
sizeof
(
buf
));
}
else
{
n
=
read
(
imclient
->
fd
,
buf
,
sizeof
(
buf
));
}
#else
/* HAVE_SSL */
n
=
read
(
imclient
->
fd
,
buf
,
sizeof
(
buf
));
#endif
/* HAVE_SSL */
if
(
n
>=
0
)
{
if
(
n
==
0
)
{
imclient_eof
(
imclient
);
}
else
{
imclient_input
(
imclient
,
buf
,
n
);
}
return
;
}
}
FD_ZERO
(
&
rfds
);
FD_ZERO
(
&
wfds
);
FD_SET
(
imclient
->
fd
,
&
rfds
);
if
(
writelen
)
FD_SET
(
imclient
->
fd
,
&
wfds
);
(
void
)
select
(
imclient
->
fd
+
1
,
&
rfds
,
&
wfds
,
(
fd_set
*
)
0
,
0
);
}
}
enum
replytype
{
replytype_inprogress
,
replytype_ok
,
replytype_no
,
replytype_bad
,
replytype_prematureok
};
struct
authresult
{
enum
replytype
replytype
;
int
r
;
};
/* Command completion callback for imclient_authenticate */
static
void
authresult
(
struct
imclient
*
imclient
__attribute__
((
unused
)),
void
*
rock
,
struct
imclient_reply
*
reply
)
{
struct
authresult
*
result
=
(
struct
authresult
*
)
rock
;
assert
(
result
);
assert
(
reply
);
if
(
!
strcmp
(
reply
->
keyword
,
"OK"
))
{
result
->
replytype
=
replytype_ok
;
}
else
if
(
!
strcmp
(
reply
->
keyword
,
"NO"
))
{
result
->
replytype
=
replytype_no
;
}
else
result
->
replytype
=
replytype_bad
;
}
/* Command completion for starttls */
static
void
tlsresult
(
struct
imclient
*
imclient
__attribute__
((
unused
)),
void
*
rock
,
struct
imclient_reply
*
reply
)
{
struct
authresult
*
result
=
(
struct
authresult
*
)
rock
;
assert
(
result
);
assert
(
reply
);
if
(
!
strcmp
(
reply
->
keyword
,
"OK"
))
{
result
->
replytype
=
replytype_ok
;
}
else
if
(
!
strcmp
(
reply
->
keyword
,
"NO"
))
{
result
->
replytype
=
replytype_no
;
}
else
result
->
replytype
=
replytype_bad
;
}
static
sasl_security_properties_t
*
make_secprops
(
int
min
,
int
max
)
{
sasl_security_properties_t
*
ret
=
(
sasl_security_properties_t
*
)
xzmalloc
(
sizeof
(
sasl_security_properties_t
));
ret
->
maxbufsize
=
IMCLIENT_BUFSIZE
;
ret
->
min_ssf
=
min
;
ret
->
max_ssf
=
max
;
return
ret
;
}
void
interaction
(
struct
imclient
*
context
,
sasl_interact_t
*
t
,
char
*
user
)
{
char
result
[
1024
];
struct
stringlist
*
cur
;
assert
(
context
);
assert
(
t
);
cur
=
malloc
(
sizeof
(
struct
stringlist
));
if
(
!
cur
)
{
t
->
len
=
0
;
t
->
result
=
NULL
;
return
;
}
cur
->
str
=
NULL
;
cur
->
next
=
context
->
interact_results
;
context
->
interact_results
=
cur
;
if
((
t
->
id
==
SASL_CB_USER
||
t
->
id
==
SASL_CB_AUTHNAME
)
&&
user
&&
user
[
0
])
{
t
->
len
=
strlen
(
user
);
cur
->
str
=
xstrdup
(
user
);
}
else
{
printf
(
"%s: "
,
t
->
prompt
);
if
(
t
->
id
==
SASL_CB_PASS
)
{
char
*
ptr
=
getpass
(
""
);
strlcpy
(
result
,
ptr
,
sizeof
(
result
));
}
else
{
fgets
(
result
,
sizeof
(
result
)
-1
,
stdin
);
result
[
strlen
(
result
)
-
1
]
=
'\0'
;
}
t
->
len
=
strlen
(
result
);
cur
->
str
=
(
char
*
)
xmalloc
(
t
->
len
+
1
);
memset
(
cur
->
str
,
0
,
t
->
len
+
1
);
memcpy
(
cur
->
str
,
result
,
t
->
len
);
}
t
->
result
=
cur
->
str
;
}
void
fillin_interactions
(
struct
imclient
*
context
,
sasl_interact_t
*
tlist
,
char
*
user
)
{
assert
(
context
);
assert
(
tlist
);
while
(
tlist
->
id
!=
SASL_CB_LIST_END
)
{
interaction
(
context
,
tlist
,
user
);
tlist
++
;
}
}
/*
* Params:
* mechlist: list of mechanisms seperated by spaces
*
* Returns:
* 0 - sucess
* 1 - failure
* 2 - severe failure?
*/
static
int
imclient_authenticate_sub
(
struct
imclient
*
imclient
,
char
*
mechlist
,
char
*
user
,
int
minssf
,
int
maxssf
,
const
char
**
mechusing
)
{
int
saslresult
;
sasl_security_properties_t
*
secprops
=
NULL
;
socklen_t
addrsize
;
struct
sockaddr_storage
saddr_l
;
struct
sockaddr_storage
saddr_r
;
char
localip
[
60
],
remoteip
[
60
];
sasl_interact_t
*
client_interact
=
NULL
;
const
char
*
out
;
unsigned
int
outlen
;
int
inlen
;
struct
authresult
result
;
assert
(
imclient
);
assert
(
mechlist
);
/*******
* Now set the SASL properties
*******/
secprops
=
make_secprops
(
minssf
,
maxssf
);
if
(
secprops
==
NULL
)
return
1
;
saslresult
=
sasl_setprop
(
imclient
->
saslconn
,
SASL_SEC_PROPS
,
secprops
);
if
(
saslresult
!=
SASL_OK
)
return
1
;
free
(
secprops
);
addrsize
=
sizeof
(
struct
sockaddr_storage
);
if
(
getpeername
(
imclient
->
fd
,(
struct
sockaddr
*
)
&
saddr_r
,
&
addrsize
)
!=
0
)
return
1
;
addrsize
=
sizeof
(
struct
sockaddr_storage
);
if
(
getsockname
(
imclient
->
fd
,(
struct
sockaddr
*
)
&
saddr_l
,
&
addrsize
)
!=
0
)
return
1
;
if
(
iptostring
((
const
struct
sockaddr
*
)
&
saddr_l
,
addrsize
,
localip
,
sizeof
(
localip
))
!=
0
)
return
1
;
if
(
iptostring
((
const
struct
sockaddr
*
)
&
saddr_r
,
addrsize
,
remoteip
,
sizeof
(
remoteip
))
!=
0
)
return
1
;
saslresult
=
sasl_setprop
(
imclient
->
saslconn
,
SASL_IPREMOTEPORT
,
remoteip
);
if
(
saslresult
!=
SASL_OK
)
return
1
;
saslresult
=
sasl_setprop
(
imclient
->
saslconn
,
SASL_IPLOCALPORT
,
localip
);
if
(
saslresult
!=
SASL_OK
)
return
1
;
/********
* SASL is setup. Now try the actual authentication
********/
saslresult
=
SASL_INTERACT
;
/* call sasl client start */
while
(
saslresult
==
SASL_INTERACT
)
{
saslresult
=
sasl_client_start
(
imclient
->
saslconn
,
mechlist
,
&
client_interact
,
&
out
,
&
outlen
,
mechusing
);
if
(
saslresult
==
SASL_INTERACT
)
{
fillin_interactions
(
imclient
,
client_interact
,
user
);
/* fill in prompts */
}
}
if
((
saslresult
!=
SASL_OK
)
&&
(
saslresult
!=
SASL_CONTINUE
))
return
saslresult
;
imclient_send
(
imclient
,
authresult
,
(
void
*
)
&
result
,
"AUTHENTICATE %a"
,
*
mechusing
);
while
(
1
)
{
/* Wait for ready response or command completion */
imclient
->
readytag
=
imclient
->
gensym
;
while
(
imclient
->
readytag
)
{
imclient_processoneevent
(
imclient
);
}
/* stop looping on command completion */
if
(
!
imclient
->
readytxt
)
break
;
if
(
isspace
((
unsigned
char
)
*
imclient
->
readytxt
))
{
inlen
=
0
;
}
else
{
inlen
=
imclient_decodebase64
(
imclient
->
readytxt
);
}
if
(
inlen
==
-1
)
{
/* bad base64 string */
return
replytype_bad
;
}
if
(
inlen
==
0
&&
outlen
>
0
)
{
/* we have something from the initial thing to send */
}
else
{
/* perform a step */
saslresult
=
SASL_INTERACT
;
while
(
saslresult
==
SASL_INTERACT
)
{
saslresult
=
sasl_client_step
(
imclient
->
saslconn
,
imclient
->
readytxt
,
inlen
,
&
client_interact
,
&
out
,
&
outlen
);
if
(
saslresult
==
SASL_INTERACT
)
{
/* fill in prompts */
fillin_interactions
(
imclient
,
client_interact
,
user
);
}
}
}
/* send our reply to the server */
if
((
saslresult
==
SASL_OK
)
||
(
saslresult
==
SASL_CONTINUE
))
{
if
(
out
==
NULL
||
outlen
==
0
)
{
imclient_write
(
imclient
,
"
\r\n
"
,
2
);
}
else
{
imclient_writebase64
(
imclient
,
out
,
outlen
);
}
}
else
{
imclient_write
(
imclient
,
"*
\r\n
"
,
3
);
return
saslresult
;
}
outlen
=
0
;
}
if
(
result
.
replytype
==
replytype_ok
)
imclient
->
saslcompleted
=
1
;
return
(
result
.
replytype
!=
replytype_ok
);
}
/* xxx service is not needed here */
int
imclient_authenticate
(
struct
imclient
*
imclient
,
char
*
mechlist
,
char
*
service
__attribute__
((
unused
)),
char
*
user
,
int
minssf
,
int
maxssf
)
{
int
r
;
char
*
mlist
;
const
char
*
mtried
;
assert
(
imclient
);
assert
(
mechlist
);
mlist
=
xstrdup
(
mechlist
);
ucase
(
mlist
);
do
{
mtried
=
NULL
;
r
=
imclient_authenticate_sub
(
imclient
,
mlist
,
user
,
minssf
,
maxssf
,
&
mtried
);
/* eliminate mtried (mechanism tried) from mlist */
if
(
r
!=
0
&&
mtried
)
{
char
*
newlist
=
xmalloc
(
strlen
(
mlist
)
+
1
);
char
*
mtr
=
xstrdup
(
mtried
);
char
*
tmp
;
ucase
(
mtr
);
tmp
=
strstr
(
mlist
,
mtr
);
if
(
!
tmp
)
{
free
(
mtr
);
free
(
mlist
);
break
;
}
*
tmp
=
'\0'
;
strcpy
(
newlist
,
mlist
);
/* Use tmp+1 here to skip the \0 we just put in.
* this is safe because even if the mechs are one character
* long there would still be another trailing \0 */
tmp
=
strchr
(
tmp
+
1
,
' '
);
if
(
tmp
)
{
tmp
++
;
/* skip the space */
strcat
(
newlist
,
tmp
);
}
free
(
mtr
);
free
(
mlist
);
mlist
=
newlist
;
}
}
while
((
r
!=
0
)
&&
(
mtried
));
if
(
r
==
0
)
{
const
void
*
maxp
;
unsigned
int
max
;
sasl_getprop
(
imclient
->
saslconn
,
SASL_MAXOUTBUF
,
&
maxp
);
max
=
*
((
const
unsigned
int
*
)
maxp
);
imclient
->
maxplain
=
max
<
IMCLIENT_BUFSIZE
?
max
:
IMCLIENT_BUFSIZE
;
}
free
(
mlist
);
return
r
;
}
#define XX 127
/*
* Tables for encoding/decoding base64
*/
static
const
char
basis_64
[]
=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
;
static
const
char
index_64
[
256
]
=
{
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
62
,
XX
,
XX
,
XX
,
63
,
52
,
53
,
54
,
55
,
56
,
57
,
58
,
59
,
60
,
61
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
0
,
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
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
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
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
XX
,
};
#define CHAR64(c) (index_64[(unsigned char)(c)])
/*
* Decode in-place the base64 data in 'input'. Returns the length
* of the decoded data, or -1 if there was an error.
*/
static
int
imclient_decodebase64
(
char
*
input
)
{
int
len
=
0
;
unsigned
char
*
output
=
(
unsigned
char
*
)
input
;
int
c1
,
c2
,
c3
,
c4
;
assert
(
input
);
while
(
*
input
)
{
c1
=
*
input
++
;
if
(
CHAR64
(
c1
)
==
XX
)
return
-1
;
c2
=
*
input
++
;
if
(
CHAR64
(
c2
)
==
XX
)
return
-1
;
c3
=
*
input
++
;
if
(
c3
!=
'='
&&
CHAR64
(
c3
)
==
XX
)
return
-1
;
c4
=
*
input
++
;
if
(
c4
!=
'='
&&
CHAR64
(
c4
)
==
XX
)
return
-1
;
*
output
++
=
(
CHAR64
(
c1
)
<<
2
)
|
(
CHAR64
(
c2
)
>>
4
);
++
len
;
if
(
c3
==
'='
)
break
;
*
output
++
=
((
CHAR64
(
c2
)
<<
4
)
&
0xf0
)
|
(
CHAR64
(
c3
)
>>
2
);
++
len
;
if
(
c4
==
'='
)
break
;
*
output
++
=
((
CHAR64
(
c3
)
<<
6
)
&
0xc0
)
|
CHAR64
(
c4
);
++
len
;
}
return
len
;
}
/*
* Write to the connection 'imclient' the base-64 encoded data
* 'output', of (unencoded) length 'len'.
*/
static
void
imclient_writebase64
(
struct
imclient
*
imclient
,
const
char
*
output
,
size_t
len
)
{
char
buf
[
1024
];
size_t
buflen
=
0
;
int
c1
,
c2
,
c3
;
assert
(
imclient
);
assert
(
output
);
while
(
len
)
{
if
(
buflen
>=
(
size_t
)(
sizeof
(
buf
)
-4
))
{
imclient_write
(
imclient
,
buf
,
buflen
);
buflen
=
0
;
}
c1
=
(
unsigned
char
)
*
output
++
;
buf
[
buflen
++
]
=
basis_64
[
c1
>>
2
];
if
(
--
len
==
0
)
c2
=
0
;
else
c2
=
(
unsigned
char
)
*
output
++
;
buf
[
buflen
++
]
=
basis_64
[((
c1
&
0x3
)
<<
4
)
|
((
c2
&
0xF0
)
>>
4
)];
if
(
len
==
0
)
{
buf
[
buflen
++
]
=
'='
;
buf
[
buflen
++
]
=
'='
;
break
;
}
if
(
--
len
==
0
)
c3
=
0
;
else
c3
=
(
unsigned
char
)
*
output
++
;
buf
[
buflen
++
]
=
basis_64
[((
c2
&
0xF
)
<<
2
)
|
((
c3
&
0xC0
)
>>
6
)];
if
(
len
==
0
)
{
buf
[
buflen
++
]
=
'='
;
break
;
}
--
len
;
buf
[
buflen
++
]
=
basis_64
[
c3
&
0x3F
];
}
if
(
buflen
>=
sizeof
(
buf
)
-2
)
{
imclient_write
(
imclient
,
buf
,
buflen
);
buflen
=
0
;
}
buf
[
buflen
++
]
=
'\r'
;
buf
[
buflen
++
]
=
'\n'
;
imclient_write
(
imclient
,
buf
,
buflen
);
}
/*************** All these functions help do the starttls; these are copied from imtest.c ********/
#ifdef HAVE_SSL
static
int
verify_depth
;
static
int
verify_error
=
X509_V_OK
;
#define CCERT_BUFSIZ 256
static
char
peer_CN
[
CCERT_BUFSIZ
];
static
char
issuer_CN
[
CCERT_BUFSIZ
];
/*
* Set up the cert things on the server side. We do need both the
* private key (in key_file) and the cert (in cert_file).
* Both files may be identical.
*
* This function is taken from OpenSSL apps/s_cb.c
*/
static
int
set_cert_stuff
(
SSL_CTX
*
ctx
,
char
*
cert_file
,
char
*
key_file
)
{
if
(
cert_file
!=
NULL
)
{
if
(
SSL_CTX_use_certificate_chain_file
(
ctx
,
cert_file
)
<=
0
)
{
printf
(
"[ unable to get certificate from '%s' ]
\n
"
,
cert_file
);
return
(
0
);
}
if
(
key_file
==
NULL
)
key_file
=
cert_file
;
if
(
SSL_CTX_use_PrivateKey_file
(
ctx
,
key_file
,
SSL_FILETYPE_PEM
)
<=
0
)
{
printf
(
"[ unable to get private key from '%s' ]
\n
"
,
key_file
);
return
(
0
);
}
/* Now we know that a key and cert have been set against
* the SSL context */
if
(
!
SSL_CTX_check_private_key
(
ctx
))
{
printf
(
"[ Private key does not match the certificate public key ]
\n
"
);
return
(
0
);
}
}
return
(
1
);
}
/* taken from OpenSSL apps/s_cb.c */
static
int
verify_callback
(
int
ok
,
X509_STORE_CTX
*
ctx
)
{
char
buf
[
256
];
X509
*
err_cert
;
int
err
;
int
depth
;
err_cert
=
X509_STORE_CTX_get_current_cert
(
ctx
);
err
=
X509_STORE_CTX_get_error
(
ctx
);
depth
=
X509_STORE_CTX_get_error_depth
(
ctx
);
X509_NAME_oneline
(
X509_get_subject_name
(
err_cert
),
buf
,
sizeof
(
buf
));
/* if (verbose==1)
printf("Peer cert verify depth=%d %s\n", depth, buf);*/
if
(
!
ok
)
{
printf
(
"verify error:num=%d:%s
\n
"
,
err
,
X509_verify_cert_error_string
(
err
));
if
(
verify_depth
>=
depth
)
{
ok
=
1
;
verify_error
=
X509_V_OK
;
}
else
{
ok
=
0
;
verify_error
=
X509_V_ERR_CERT_CHAIN_TOO_LONG
;
}
}
switch
(
ctx
->
error
)
{
case
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
:
X509_NAME_oneline
(
X509_get_issuer_name
(
ctx
->
current_cert
),
buf
,
sizeof
(
buf
));
printf
(
"issuer= %s
\n
"
,
buf
);
break
;
case
X509_V_ERR_CERT_NOT_YET_VALID
:
case
X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD
:
printf
(
"cert not yet valid
\n
"
);
break
;
case
X509_V_ERR_CERT_HAS_EXPIRED
:
case
X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD
:
printf
(
"cert has expired
\n
"
);
break
;
}
/* if (verbose==1)
printf("verify return:%d\n", ok);*/
return
(
ok
);
}
/* taken from OpenSSL apps/s_cb.c */
static
RSA
*
tmp_rsa_cb
(
SSL
*
s
__attribute__
((
unused
)),
int
export
__attribute__
((
unused
)),
int
keylength
)
{
static
RSA
*
rsa_tmp
=
NULL
;
if
(
rsa_tmp
==
NULL
)
{
rsa_tmp
=
RSA_generate_key
(
keylength
,
RSA_F4
,
NULL
,
NULL
);
}
return
(
rsa_tmp
);
}
/*
* Seed the random number generator.
*/
static
int
tls_rand_init
(
void
)
{
#ifdef EGD_SOCKET
return
(
RAND_egd
(
EGD_SOCKET
));
#else
/* otherwise let OpenSSL do it internally */
return
0
;
#endif
}
/*
* This is the setup routine for the SSL client.
*
* The skeleton of this function is taken from OpenSSL apps/s_client.c.
*/
static
int
tls_init_clientengine
(
struct
imclient
*
imclient
,
int
verifydepth
,
char
*
var_tls_cert_file
,
char
*
var_tls_key_file
,
char
*
var_tls_CAfile
,
char
*
var_tls_CApath
)
{
int
off
=
0
;
int
verify_flags
=
SSL_VERIFY_NONE
;
char
*
CApath
;
char
*
CAfile
;
char
*
c_cert_file
;
char
*
c_key_file
;
assert
(
imclient
);
SSL_load_error_strings
();
SSLeay_add_ssl_algorithms
();
if
(
tls_rand_init
()
==
-1
)
{
printf
(
"[ TLS engine: cannot seed PRNG ]
\n
"
);
return
-1
;
}
imclient
->
tls_ctx
=
SSL_CTX_new
(
TLSv1_client_method
());
if
(
imclient
->
tls_ctx
==
NULL
)
{
return
-1
;
};
off
|=
SSL_OP_ALL
;
/* Work around all known bugs */
SSL_CTX_set_options
(
imclient
->
tls_ctx
,
off
);
/* debugging SSL_CTX_set_info_callback(imclient->tls_ctx, apps_ssl_info_callback); */
if
(
var_tls_CAfile
==
NULL
||
strlen
(
var_tls_CAfile
)
==
0
)
CAfile
=
NULL
;
else
CAfile
=
var_tls_CAfile
;
if
(
var_tls_CApath
==
NULL
||
strlen
(
var_tls_CApath
)
==
0
)
CApath
=
NULL
;
else
CApath
=
var_tls_CApath
;
if
(
CAfile
||
CApath
)
if
((
!
SSL_CTX_load_verify_locations
(
imclient
->
tls_ctx
,
CAfile
,
CApath
))
||
(
!
SSL_CTX_set_default_verify_paths
(
imclient
->
tls_ctx
)))
{
printf
(
"[ TLS engine: cannot load CA data ]
\n
"
);
return
-1
;
}
if
(
var_tls_cert_file
==
NULL
||
strlen
(
var_tls_cert_file
)
==
0
)
c_cert_file
=
NULL
;
else
c_cert_file
=
var_tls_cert_file
;
if
(
var_tls_key_file
==
NULL
||
strlen
(
var_tls_key_file
)
==
0
)
c_key_file
=
NULL
;
else
c_key_file
=
var_tls_key_file
;
if
(
c_cert_file
||
c_key_file
)
if
(
!
set_cert_stuff
(
imclient
->
tls_ctx
,
c_cert_file
,
c_key_file
))
{
printf
(
"[ TLS engine: cannot load cert/key data ]
\n
"
);
return
-1
;
}
SSL_CTX_set_tmp_rsa_callback
(
imclient
->
tls_ctx
,
tmp_rsa_cb
);
verify_depth
=
verifydepth
;
SSL_CTX_set_verify
(
imclient
->
tls_ctx
,
verify_flags
,
verify_callback
);
return
0
;
}
#if 0
/* Dead code only for debugging */
static int do_dump = 1;
/*
* taken from OpenSSL crypto/bio/b_dump.c, modified to save a lot of strcpy
* and strcat by Matti Aarnio.
*/
#define TRUNCATE
#define DUMP_WIDTH 16
static int tls_dump(const char *s, int len)
{
int ret = 0;
char buf[160 + 1];
char *ss;
int i;
int j;
int rows;
int trunc;
unsigned char ch;
trunc = 0;
#ifdef TRUNCATE
for (; (len > 0) && ((s[len - 1] == ' ') || (s[len - 1] == '\0')); len--)
trunc++;
#endif
rows = (len / DUMP_WIDTH);
if ((rows * DUMP_WIDTH) < len)
rows++;
for (i = 0; i < rows; i++) {
buf[0] = '\0'; /* start with empty string */
ss = buf;
sprintf(ss, "%04x ", i * DUMP_WIDTH);
ss += strlen(ss);
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len) {
strcpy(ss, " ");
} else {
ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j))
& 0xff;
sprintf(ss, "%02x[%c]%c", ch, ch, j == 7 ? '|' : ' ');
ss += 6;
}
}
ss += strlen(ss);
*ss+= ' ';
for (j = 0; j < DUMP_WIDTH; j++) {
if (((i * DUMP_WIDTH) + j) >= len)
break;
ch = ((unsigned char) *((char *) (s) + i * DUMP_WIDTH + j)) & 0xff;
*ss+= (((ch >= ' ') && (ch <= '~')) ? ch : '.');
if (j == 7) *ss+= ' ';
}
*ss = 0;
/*
* if this is the last call then update the ddt_dump thing so that
* we will move the selection point in the debug window
*/
printf("%s\n", buf);
ret += strlen(buf);
}
#ifdef TRUNCATE
if (trunc > 0) {
sprintf(buf, "%04x - <SPACES/NULS>\n", len+ trunc);
printf("%s\n", buf);
ret += strlen(buf);
}
#endif
return (ret);
}
/* these next two taken from OpenSSL apps/s_cb.c */
static long bio_dump_cb(BIO * bio,
int cmd,
const char *argp,
int argi,
long argl __attribute__((unused)),
long ret)
{
if (!do_dump)
return (ret);
if (cmd == (BIO_CB_READ | BIO_CB_RETURN)) {
printf("read from %08X [%08lX] (%d bytes => %ld (0x%X))\n", (unsigned int) bio,
(unsigned long) argp,
argi, ret, (unsigned int) ret);
tls_dump(argp, (int) ret);
return (ret);
} else if (cmd == (BIO_CB_WRITE | BIO_CB_RETURN)) {
printf("write to %08X [%08lX] (%d bytes => %ld (0x%X))\n", (unsigned int) bio,
(unsigned long) argp,
argi, ret, (unsigned int) ret);
tls_dump(argp, (int) ret);
}
return (ret);
}
static void apps_ssl_info_callback(SSL * s, int where, int ret)
{
char *str;
int w;
w = where & ~SSL_ST_MASK;
if (w & SSL_ST_CONNECT)
str = "SSL_connect";
else if (w & SSL_ST_ACCEPT)
str = "SSL_accept";
else
str = "undefined";
if (where & SSL_CB_LOOP) {
printf("%s:%s\n", str, SSL_state_string_long(s));
} else if (where & SSL_CB_ALERT) {
str = (where & SSL_CB_READ) ? "read" : "write";
if ((ret & 0xff) != SSL3_AD_CLOSE_NOTIFY)
printf("SSL3 alert %s:%s:%s\n", str,
SSL_alert_type_string_long(ret),
SSL_alert_desc_string_long(ret));
} else if (where & SSL_CB_EXIT) {
if (ret == 0)
printf("%s:failed in %s\n",
str, SSL_state_string_long(s));
else if (ret < 0) {
printf("%s:error in %s %i\n",
str, SSL_state_string_long(s),ret);
}
}
}
#endif
int
tls_start_clienttls
(
struct
imclient
*
imclient
,
unsigned
*
layer
,
char
**
authid
,
int
fd
)
{
int
sts
;
SSL_SESSION
*
session
;
SSL_CIPHER
*
cipher
;
X509
*
peer
;
const
char
*
tls_protocol
=
NULL
;
const
char
*
tls_cipher_name
=
NULL
;
int
tls_cipher_usebits
=
0
;
int
tls_cipher_algbits
=
0
;
char
*
tls_peer_CN
=
""
;
char
*
tls_issuer_CN
=
NULL
;
if
(
imclient
->
tls_conn
==
NULL
)
{
imclient
->
tls_conn
=
(
SSL
*
)
SSL_new
(
imclient
->
tls_ctx
);
}
if
(
imclient
->
tls_conn
==
NULL
)
{
printf
(
"Could not allocate 'con' with SSL_new()
\n
"
);
return
-1
;
}
SSL_clear
(
imclient
->
tls_conn
);
if
(
!
SSL_set_fd
(
imclient
->
tls_conn
,
fd
))
{
printf
(
"SSL_set_fd failed
\n
"
);
return
-1
;
}
/*SSL_set_read_ahead(imclient->tls_conn, 1);*/
/*
* This is the actual handshake routine. It will do all the negotiations
* and will check the client cert etc.
*/
SSL_set_connect_state
(
imclient
->
tls_conn
);
/*
* We do have an SSL_set_fd() and now suddenly a BIO_ routine is called?
* Well there is a BIO below the SSL routines that is automatically
* created for us, so we can use it for debugging purposes.
*/
/* if (verbose==1) */
/* BIO_set_callback(SSL_get_rbio(imclient->tls_conn), bio_dump_cb);*/
/* Dump the negotiation for loglevels 3 and 4 */
if
((
sts
=
SSL_connect
(
imclient
->
tls_conn
))
<=
0
)
{
printf
(
"[ SSL_connect error %d ]
\n
"
,
sts
);
/* xxx get string error? */
session
=
SSL_get_session
(
imclient
->
tls_conn
);
if
(
session
)
{
SSL_CTX_remove_session
(
imclient
->
tls_ctx
,
session
);
printf
(
"[ SSL session removed ]
\n
"
);
}
if
(
imclient
->
tls_conn
!=
NULL
)
SSL_free
(
imclient
->
tls_conn
);
imclient
->
tls_conn
=
NULL
;
return
-1
;
}
/*
* Lets see, whether a peer certificate is availabe and what is
* the actual information. We want to save it for later use.
*/
peer
=
SSL_get_peer_certificate
(
imclient
->
tls_conn
);
if
(
peer
!=
NULL
)
{
X509_NAME_get_text_by_NID
(
X509_get_subject_name
(
peer
),
NID_commonName
,
peer_CN
,
CCERT_BUFSIZ
);
tls_peer_CN
=
peer_CN
;
X509_NAME_get_text_by_NID
(
X509_get_issuer_name
(
peer
),
NID_commonName
,
issuer_CN
,
CCERT_BUFSIZ
);
/* if (verbose==1)
printf("subject_CN=%s, issuer_CN=%s\n", peer_CN, issuer_CN);*/
tls_issuer_CN
=
issuer_CN
;
}
tls_protocol
=
SSL_get_version
(
imclient
->
tls_conn
);
cipher
=
SSL_get_current_cipher
(
imclient
->
tls_conn
);
tls_cipher_name
=
SSL_CIPHER_get_name
(
cipher
);
tls_cipher_usebits
=
SSL_CIPHER_get_bits
(
cipher
,
&
tls_cipher_algbits
);
if
(
layer
!=
NULL
)
*
layer
=
tls_cipher_usebits
;
if
(
authid
!=
NULL
)
*
authid
=
tls_peer_CN
;
/* printf("TLS connection established: %s with cipher %s (%d/%d bits)\n",
tls_protocol, tls_cipher_name,
tls_cipher_usebits, tls_cipher_algbits);*/
return
0
;
}
#endif
/* HAVE_SSL */
int
imclient_havetls
()
{
#ifdef HAVE_SSL
return
1
;
#else
return
0
;
#endif
}
int
imclient_starttls
(
struct
imclient
*
imclient
,
char
*
cert_file
,
char
*
key_file
,
char
*
CAfile
,
char
*
CApath
)
{
#ifdef HAVE_SSL
int
result
;
struct
authresult
theresult
;
unsigned
ssf
;
char
*
auth_id
;
imclient_send
(
imclient
,
tlsresult
,
(
void
*
)
&
theresult
,
"STARTTLS"
);
/* Wait for ready response or command completion */
imclient
->
readytag
=
imclient
->
gensym
;
while
(
imclient
->
readytag
)
{
imclient_processoneevent
(
imclient
);
}
result
=
tls_init_clientengine
(
imclient
,
10
,
cert_file
,
key_file
,
CAfile
,
CApath
);
if
(
result
!=
0
)
{
printf
(
"[ TLS engine failed ]
\n
"
);
return
1
;
}
else
{
result
=
tls_start_clienttls
(
imclient
,
&
ssf
,
&
auth_id
,
imclient
->
fd
);
if
(
result
!=
0
)
{
printf
(
"[ TLS negotiation did not succeed ]
\n
"
);
return
1
;
}
}
/* turn non-blocking i/o back on */
/* TLS negotiation succeeded */
imclient
->
tls_on
=
1
;
auth_id
=
""
;
/* xxx this really should be peer_CN or
issuer_CN but I can't figure out which is
which at the moment */
/* tell SASL about the negotiated layer */
result
=
sasl_setprop
(
imclient
->
saslconn
,
SASL_SSF_EXTERNAL
,
&
ssf
);
if
(
result
!=
SASL_OK
)
return
1
;
result
=
sasl_setprop
(
imclient
->
saslconn
,
SASL_AUTH_EXTERNAL
,
auth_id
);
if
(
result
!=
SASL_OK
)
return
1
;
return
0
;
#else
printf
(
"[ TLS support not present (imclient_starttls) ]
\n
"
);
return
1
;
#endif
/* HAVE_SSL */
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Apr 6, 2:24 AM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18832022
Default Alt Text
imclient.c (53 KB)
Attached To
Mode
R111 cyrus-imapd
Attached
Detach File
Event Timeline