-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Canna Security Advisory

Canna-2002-1  multiple vulnerabilities in canna server

Date Reported: 06 Nov 2002
Release Date: 02 Dec 2002
Vulnerable: Yes
Security database references:
More information:

Vulnerability #1

"hsj" of Shadow Penguin Security discovered a heap overflow
vulnerability in the irw_through function in canna server
version 3.6 and earlier.

A malicious remote user could exploit this vulnerability and
potentially gain privileges of running canna server
or cause a denial of service attack.

Vulnerability #2

AIDA Shinra of Canna project found lack of validations of requests
in canna version 3.6 and earlier.

A malicious remote user can exploit this vulnerability and
peep some information, or cause a denial of service attack. An
attacker might take advantage of this information to exploit the
first vulnerability.

Workaround:

1. Stop canna server by cannakill command. In addition, if your
server version is 3.5b2 or earlier, you need to drop setuid/setgid
bit or uninstall the server.

2. Create /etc/hosts.canna file and restrict access from untrusted
hosts. Note that access control by username does not help. If one
user of a host is untrusted, the host should be considered as a
totally untrusted host. Additionally, this workaround is effective
only to the first vulnerability. You cannot workaround the second by
this access control.

3. If your server version is 3.6 and all local users are trusted,
remove -inet option when you start the server.

Solution:

Please upgrade your canna server to version 3.6p1 or
apply the following patches.

ChangeLog:
02 Dec 2002: Initial revision
02 Dec 2002:
 * correct Shadow Pengin Society to Shadow Penguin Security.
 * add release date

Canna ƥ

Canna-2002-1 ʣcanna Сȼ

: 2002ǯ116
ɽ:   2002ǯ122

ȼ #1

Shadow Penguin Security  hsj ϡС 3.6 canna С
 irw_through ؿ˴ޤޤҡץХեȼȯޤ

դΤ⡼ȥ桼ϤȼѤcanna С
餻Ƥ桼¤뤫⤷ޤ󡣤ޤӥǽ
θȤʤ뤫⤷ޤ

ȼ #2

canna ץȤ AIDA Shinra ϡС3.6 canna С
ǡꥯȤθڤԽʬǤ뤳Ȥȯޤ

դΤ⡼ȥ桼ϤȼѤcanna С
ξ뤳ȤǤޤξϡȼ
뤿ˤѤ뤫Τޤ



1. cannakill ޥɤ canna СߤƤ˲äơ
3.5b2 ΥСǤϡsetuid/setgidӥåȤȤС
󥤥󥹥ȡ뤹ɬפޤ

2. /etc/hosts.cannaäơѤǤʤۥȤ³ݤ
ꤷƤκݡ桼̾ˤ¤ϲˤʤ餺ۥ
Ȥˤ¤ͭǤͤǤ⿮Ǥʤ桼¸ߤۥ
ȤǤʤۥȤȹͤ٤Ǥޤβ
ˤΤͭǤꡢȼϤˡǤϲǤޤ

3. СΥС 3.6 ǡΥ桼Ǥ硢
canna Сεưץ󤫤-inet򳰤Ƥ

canna С С3.6p1 ʹߤ˹뤫ʲΥѥåŬ
Ƥ

:
02 Dec 2002: ǽȯɽ
02 Dec 2002:
 * Shadow Penguin Society  Shadow Penguin Security 
 * ɵ

Index: server/convert.c
===================================================================
RCS file: /cvsroot/canna/canna/server/convert.c,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 convert.c
- --- server/convert.c	19 Oct 2002 08:27:53 -0000	1.1.1.1
+++ server/convert.c	1 Dec 2002 10:54:39 -0000
@@ -53,6 +53,8 @@
 #define ACK2 2
 #define ACK3 3
 #define CHECK_ACK_BUF_SIZE	(ACK_BUFSIZE + (SIZEOFLONG * 2) )
+#define IR_INT_MAX 32767
+#define IR_INT_INVAL(x) ((unsigned int)x > IR_INT_MAX)
 
 extern int  errno;
 
@@ -1778,6 +1780,8 @@
 	return( needsize ) ;
 
     req->namelen = (int)L4TOL(buf + SIZE4);
+    if( IR_INT_INVAL(req->namelen) )
+	return( -1 );
    ir_debug( Dmsg(10,"req->namelen =%d\n", req->namelen ); )
 
     if( (needsize = SIZE8 + req->namelen - size) > 0 )
@@ -1785,6 +1789,8 @@
 
     if( req->namelen > 0 ){
 	req->name = buf + SIZE8 ;
+	if( req->name[req->namelen - 1] != 0 )
+	    return( -1 );
     }
    ir_debug( Dmsg(10,"req->namelen =%d\n", req->namelen ); )
    ir_debug( Dmsg(10,"req->name =%s\n", req->name ); )
Index: server/util.c
===================================================================
RCS file: /cvsroot/canna/canna/server/util.c,v
retrieving revision 1.3.2.1
diff -u -r1.3.2.1 util.c
- --- server/util.c	7 Nov 2002 14:46:02 -0000	1.3.2.1
+++ server/util.c	1 Dec 2002 10:54:39 -0000
@@ -217,6 +217,19 @@
   return res;
 }
 
+const Ushort *
+ushortmemchr(ws, ch, len)
+const Ushort *ws;
+int ch;
+size_t len;
+{
+  const Ushort *p, *end;
+  for (p = ws, end = ws + len; p < end; ++p)
+      if (*p == (Ushort)ch)
+	  return p;
+  return NULL;
+}
+
 int
 ushortstrcpy(wd, ws)
 Ushort *wd, *ws;
Index: server/wconvert.c
===================================================================
RCS file: /cvsroot/canna/canna/server/wconvert.c,v
retrieving revision 1.2
diff -u -r1.2 wconvert.c
- --- server/wconvert.c	20 Oct 2002 04:10:30 -0000	1.2
+++ server/wconvert.c	1 Dec 2002 10:54:40 -0000
@@ -100,6 +100,7 @@
 #endif
 
 extern void DispDebug() ;
+extern const Ushort *ushortmemchr pro((const Ushort *, int, size_t));
 extern int canna_server_hi ;
 extern int canna_server_lo ;
 #ifdef DEBUG
@@ -1333,7 +1334,10 @@
     char *dirname, *dirnamelong = (char *)0;
     int cxnum = Request.type18.context, stat = -1;
     int requestsize = Request.type18.size, retval;
+    size_t datasize = Request.type18.datalen - SIZEOFSHORT * 2;
 
+    if (datasize == 0 || req->data[datasize - 1] != 0)
+      goto protoerr;
     if (validcontext(cxnum, client, wListDictionary)) {
       if (requestsize <= sizeof(local_buffer) ||
 	  (dicnames = malloc(requestsize))) {
@@ -1370,6 +1374,7 @@
       }
     }	
 
+protoerr:
     retval = SendType6Reply(client, wListDictionary, EXTPROTO, stat,
 			    dicnames, namesize(dicnames, stat));
     if (dicnames != (char *)local_buffer) free(dicnames);
@@ -1481,10 +1486,15 @@
     char *dicname, *dirname, *dirnamelong = (char *)0;
     int cxnum = Request.type18.context, stat = BADCONT;
     int dirlen, requestsize = Request.type18.size, retval;
+    size_t datasize = Request.type18.datalen - SIZEOFSHORT * 2;
 
+    if (datasize == 0 || req->data[datasize - 1] != 0)
+      goto protoerr;
     if (validcontext(cxnum, client, wGetWordTextDictionary)) {
 	dirname = req->data ;
 	dirlen = strlen(dirname) + 1 ;
+	if (dirlen == datasize)
+	  goto protoerr;
 	dicname = &(req->data[dirlen]) ;
 	if (dirlen > 1) {
 	  if (!dirname || dirname[0] != ':' ||
@@ -1526,6 +1536,7 @@
 	  free(dirnamelong);
 	}
     }
+protoerr:
     retval = SendType7Reply(client, wGetWordTextDictionary, EXTPROTO,
 			    stat, stat > 0 ? stat + 1 : 0, infobuf);
     if (infobuf != (Ushort *)local_buffer) free((char *)infobuf);
@@ -2307,6 +2318,9 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq1 start!!\n") );
 
+    if (Request.type1.datalen != 0)
+	return( -1 );
+
     return( 0 ) ;
 }
 
@@ -2316,6 +2330,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq2 start!!\n") );
 
+    if (Request.type2.datalen != SIZEOFSHORT)
+	return( -1 );
     buf += HEADER_SIZE; Request.type2.context = S2TOS(buf);
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type2.context) );
 
@@ -2328,6 +2344,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq3 start!!\n") );
 
+    if (Request.type3.datalen != SIZEOFSHORT * 2)
+	return( -1 );
     buf += HEADER_SIZE; Request.type3.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type3.buflen = S2TOS(buf);
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type3.context) );
@@ -2345,12 +2363,18 @@
 
     ir_debug( Dmsg(10, "ProcWideReq4 start!!\n") );
 
+    if (Request.type4.datalen < SIZEOFSHORT * 4)
+	return( -1 );
     buf += HEADER_SIZE; Request.type4.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type4.begin = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type4.end = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type4.yomilen = S2TOS(buf);
     Request.type4.yomi = (Ushort *)(buf += SIZEOFSHORT) ;
- -    len = Request.type4.datalen - SIZEOFSHORT * 4;
+    len = Request.type4.yomilen + 1;
+    if (Request.type4.datalen != SIZEOFSHORT * (4 + len)
+	    || len == 0
+	    || Request.type4.yomi[len - 1] != 0)
+	return( -1 );
     for (data = Request.type4.yomi, i = 0; i < len; i++, data++)
 	*data = ntohs((unsigned short)*data); /* äȤʤ */
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type4.context) );
@@ -2370,6 +2394,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq5 start!!\n") );
 
+    if (Request.type5.datalen != SIZEOFSHORT * 2 + SIZEOFINT)
+	return( -1 );
     buf += HEADER_SIZE; Request.type5.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type5.size = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type5.mode = L4TOL(buf);
@@ -2386,6 +2412,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq6 start!!\n") );
 
+    if (Request.type6.datalen != SIZEOFSHORT * 3)
+	return( -1 );
     buf += HEADER_SIZE; Request.type6.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type6.number = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type6.buflen = S2TOS(buf);
@@ -2402,6 +2430,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq7 start!!\n") );
 
+    if (Request.type7.datalen != SIZEOFSHORT * 3)
+	return( -1 );
     buf += HEADER_SIZE; Request.type7.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type7.number = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type7.yomilen = (short)S2TOS(buf);
@@ -2418,6 +2448,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq8 start!!\n") );
 
+    if (Request.type8.datalen != SIZEOFSHORT * 4)
+	return( -1 );
     buf += HEADER_SIZE; Request.type8.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type8.curbun = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type8.curkouho = S2TOS(buf);
@@ -2436,6 +2468,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq9 start!!\n") );
 
+    if (Request.type9.datalen != SIZEOFSHORT * 4)
+	return( -1 );
     buf += HEADER_SIZE; Request.type9.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type9.number = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type9.kouho = S2TOS(buf);
@@ -2453,9 +2487,13 @@
 BYTE *buf ;
 {
     register int i ;
+    int rest;
 
     ir_debug( Dmsg(10, "ProcWideReq10 start!!\n") );
 
+    rest = Request.type10.datalen - (SIZEOFSHORT * 2 + SIZEOFINT);
+    if (rest < 0)
+	return( -1 );
     buf += HEADER_SIZE; Request.type10.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type10.number = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type10.mode = L4TOL(buf);
@@ -2463,6 +2501,8 @@
     ir_debug( Dmsg(10, "req->number =%d\n", Request.type10.number) );
     ir_debug( Dmsg(10, "req->mode =%d\n", Request.type10.mode) );
 
+    if (rest != Request.type10.number * SIZEOFSHORT)
+	return( -1 );
     buf += SIZEOFINT; Request.type10.kouho = (short *)buf; /* short? */
     for (i = 0; i < Request.type10.number; i++) {
       Request.type10.kouho[i] = S2TOS(buf); buf += SIZEOFSHORT;
@@ -2479,12 +2519,18 @@
     register Ushort *data;
     int i, len ;
 
- -    ir_debug( Dmsg(10, "ProcWideReq10 start!!\n") );
+    ir_debug( Dmsg(10, "ProcWideReq11 start!!\n") );
 
+    if (Request.type11.datalen < SIZEOFSHORT * 2)
+	return( -1 );
     buf += HEADER_SIZE; Request.type11.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type11.curbun = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type11.yomi = (Ushort *)buf;
+    if (Request.type11.datalen % SIZEOFSHORT != 0)
+	return( -1 );
     len = ((int)Request.type11.datalen - SIZEOFSHORT * 2) / SIZEOFSHORT ;
+    if (len == 0 || Request.type11.yomi[len - 1] != 0)
+	return( -1 );
     for (data = Request.type11.yomi, i = 0; i < len; i++, data++)
 	*data = ntohs( *data ); /* ʤ󤫤 */
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type11.context) );
@@ -2501,16 +2547,26 @@
 BYTE *buf ;
 {
     register Ushort *data;
- -    int i, len ;
+    int i, len, rest;
 
     ir_debug( Dmsg(10, "ProcWideReq12 start!!\n") );
 
+    rest = Request.type12.datalen - SIZEOFSHORT;
+    if (rest < 0)
+	return( -1 );
     buf += HEADER_SIZE; Request.type12.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type12.datainfo = (Ushort *)buf;
+    if (!ushortmemchr((Ushort *)buf, 0, rest / SIZEOFSHORT))
+	return( -1 );
     len = ushortstrlen((Ushort *)buf) + 1;
+    rest -= len * SIZEOFSHORT;
+    if (rest <= 0)
+	return( -1 );
     for( data = Request.type12.datainfo, i = 0; i < len; i++, data++ )
 	*data = ntohs( *data ); /* ʤ󤫤 */
     buf += len * SIZEOFSHORT;
+    if (buf[rest - 1] != '\0')
+	return( -1 );
     Request.type12.dicname = (char *)buf;
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type12.context) );
     ir_debug( Dmsg(10, "req->datainfo =%s\n",
@@ -2528,24 +2584,37 @@
 BYTE *buf ;
 {
     register Ushort *data;
- -    int i ,len ;
+    int i ,len, rest;
 
     ir_debug( Dmsg(10, "ProcWideReq13 start!!\n") );
 
+    rest = Request.type13.datalen - SIZEOFSHORT;
+    if (rest < 0)
+	return( -1 );
     buf += HEADER_SIZE; Request.type13.context = S2TOS(buf);
     len = SIZEOFSHORT ;
     buf += len;
     Request.type13.dicname = (char *)buf;
+    if (!memchr(buf, 0, rest))
+	return( -1 );
     len = strlen( (char *)buf ) + 1;
+    rest -= len;
+    if (rest % SIZEOFSHORT
+	    || rest < SIZEOFSHORT * 3)
+	return( -1 );
     buf += len;
     Request.type13.yomi = (Ushort *)buf;
     len = ((int)Request.type13.datalen - len - SIZEOFSHORT * 4) / SIZEOFSHORT;
+    if (ushortmemchr((Ushort *)buf, 0, len) != (Ushort *)buf + len - 1)
+	return( -1 );
     for( data = Request.type13.yomi, i = 0; i < len; i++, data++)
 	*data = ntohs( *data );
- -    buf += (ushortstrlen((Ushort *)buf) + 1) * SIZEOFSHORT;
+    buf += len * SIZEOFSHORT;
     Request.type13.yomilen = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type13.kouhosize = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type13.hinshisize = S2TOS(buf);
+    if (Request.type13.yomilen != len - 1)
+	return( -1 );
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type13.context) );
     ir_debug( Dmsg(10, "req->dicname =%s\n", Request.type13.dicname) );
     ir_debug( Dmsg(10, "req->yomi =%s\n",
@@ -2567,11 +2636,16 @@
 
     ir_debug( Dmsg(10, "ProcWideReq14 start!!\n") );
 
+    if (Request.type14.datalen <= SIZEOFINT + SIZEOFSHORT
+	    || Request.type14.datalen % SIZEOFSHORT)
+	return( -1 );
     buf += HEADER_SIZE; Request.type14.mode = L4TOL(buf);
     buf += SIZEOFINT;   Request.type14.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type14.yomi = (Ushort *)buf;
     len = ((int)Request.type14.datalen - SIZEOFSHORT - SIZEOFINT)
       / SIZEOFSHORT;
+    if (Request.type14.yomi[len - 1] != 0)
+	return( -1 );
     for (data = Request.type14.yomi, i = 0; i < len; i++, data++)
 	*data = ntohs( *data ); /* ʤ󤫤 */
 
@@ -2588,11 +2662,17 @@
 ProcWideReq15(buf)
 BYTE *buf ;
 {
+    int rest;
     ir_debug( Dmsg(10, "ProcWideReq15 start!!\n") );
 
+    rest = Request.type15.datalen - (SIZEOFINT + SIZEOFSHORT);
+    if (rest <= 0)
+	return( -1 );
     buf += HEADER_SIZE; Request.type15.mode = L4TOL(buf);
     buf += SIZEOFINT;   Request.type15.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type15.dicname = (char *)buf;
+    if (buf[rest - 1] != 0)
+	return( -1 );
     ir_debug( Dmsg(10, "req->mode =%d\n", Request.type15.mode) );
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type15.context) );
     ir_debug( Dmsg(10, "req->dicname =%s\n",
@@ -2608,6 +2688,9 @@
     ir_debug( Dmsg(10, "ProcWideReq17 start!!\n") );
 
     buf += HEADER_SIZE;
+    if (Request.type17.datalen < SIZEOFCHAR * 2
+	    || buf[Request.type17.datalen - SIZEOFCHAR * 2] != 0)
+	return( -1 );
     Request.type17.dicname = (char *)buf;
     Request.type17.mode = (char)*(buf + Request.type17.datalen - SIZEOFCHAR) ;
     ir_debug( Dmsg(10, "req->dicname =%s\n",
@@ -2624,6 +2707,8 @@
 {
     ir_debug( Dmsg(10, "ProcWideReq18 start!!\n") );
 
+    if (Request.type18.datalen < SIZEOFSHORT * 2)
+	return( -1 );
     buf += HEADER_SIZE; Request.type18.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type18.data = (char *)buf;
     buf += Request.type18.datalen - SIZEOFSHORT * 2;
@@ -2641,12 +2726,18 @@
 ProcWideReq19(buf)
 BYTE *buf ;
 {
+    int rest;
     ir_debug( Dmsg(10, "ProcWideReq19 start!!\n") );
 
+    rest = Request.type20.datalen - (SIZEOFSHORT + SIZEOFINT * 2);
+    if (rest < 0)
+	return( -1 );
     buf += HEADER_SIZE; Request.type20.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type20.command = L4TOL(buf);
     buf += SIZEOFINT;   Request.type20.bufsize = L4TOL(buf);
     buf += SIZEOFINT;   Request.type20.buf = (char *)buf;
+    if (Request.type20.bufsize != rest)
+	return( -1 );
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type20.context) );
     ir_debug( Dmsg(10, "req->command =%d\n", Request.type20.command) );
     ir_debug( Dmsg(10, "req->bufsize =%d\n", Request.type20.bufsize) );
@@ -2658,15 +2749,25 @@
 ProcWideReq20(buf)
 BYTE *buf ;
 {
+    BYTE *bufend;
     ir_debug( Dmsg(10, "ProcWideReq20 start!!\n") );
 
+    if (Request.type21.datalen < SIZEOFINT + SIZEOFSHORT)
+	return( -1 );
     buf += HEADER_SIZE; Request.type21.mode = L4TOL(buf);
+    bufend = buf + Request.type21.datalen;
     buf += SIZEOFINT;   Request.type21.context = S2TOS(buf);
     buf += SIZEOFSHORT; Request.type21.dirname = (char *)buf;
+    if (!memchr(buf, 0, bufend - buf))
+	return( -1 );
     buf += strlen((char *)buf) + 1;
     Request.type21.srcdic = (char *)buf;
+    if (!memchr(buf, 0, bufend - buf))
+	return( -1 );
     buf += strlen((char *)buf) + 1;
     Request.type21.dstdic = (char *)buf;
+    if (*(bufend - 1) != 0)
+	return( -1 );
 
     ir_debug( Dmsg(10, "req->mode =%d\n", Request.type21.mode) );
     ir_debug( Dmsg(10, "req->context =%d\n", Request.type21.context) );
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (FreeBSD)

iD8DBQE961n64ac+GaESVJARAr8RAJ4oNybDZ/pLssGG3fC2cBvFaDWBCwCfScbw
EWujlY/ZRefv7WOuMAUzkQg=
=JVwB
-----END PGP SIGNATURE-----
