Bug 155003 (CVE-2006-2622) - AUDIT-0: novell-lum: eDirectory client support
Summary: AUDIT-0: novell-lum: eDirectory client support
Status: RESOLVED FIXED
Alias: CVE-2006-2622
Product: SUSE Security Incidents
Classification: Novell Products
Component: Incidents (show other bugs)
Version: unspecified
Hardware: Other Other
: P5 - None : Normal
Target Milestone: ---
Assignee: Alexander Danoyan
QA Contact: Security Team bot
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-03-03 15:04 UTC by Marcus Meissner
Modified: 2017-03-07 16:28 UTC (History)
4 users (show)

See Also:
Found By: Other
Services Priority:
Business Priority:
Blocker: ---
Marketing QA Status: ---
IT Deployment: ---


Attachments
0_SCAN.lint (137.69 KB, text/plain)
2006-03-24 08:56 UTC, Thomas Biege
Details
0_SCAN_2.splint (77.89 KB, text/plain)
2006-03-24 14:26 UTC, Thomas Biege
Details
0_SCAN_2_stripped.splint (2.06 KB, text/plain)
2006-03-27 13:29 UTC, Thomas Biege
Details
0_SCAN.splint (1.24 MB, text/plain)
2006-03-29 16:27 UTC, Thomas Biege
Details
novell-lum.diff (10.25 KB, patch)
2006-04-05 09:25 UTC, Thomas Biege
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Marcus Meissner 2006-03-03 15:04:53 UTC
If eDirectory client support gets towards SLES 10 via Nuernberg Autobuild
we need to audit it.

novell-lum          (PAM components)

novell-NLDAPsdk     (libraries for above)
Comment 1 Thomas Biege 2006-03-13 14:28:01 UTC
Will have a look.
Comment 2 Thomas Biege 2006-03-15 18:06:24 UTC
> find . -name "*.c" -exec grep --with-filename strcpy {} \; | wc -l
417
> find . -name "*.c" -exec grep --with-filename strcat {} \; | wc -l
62
> find . -name "*.c" -exec grep --with-filename sprintf {} \; | wc -l
254
> find . -name "*.c" -exec grep --with-filename "/tmp" {} \; | wc -l
9
> find . -name "*.c" -exec grep --with-filename -E "system\(.*\)" {} \;  | wc -l
29


Too much usage of high-profile (security-wise) functions.
Comment 3 Thomas Biege 2006-03-15 18:07:59 UTC
The above is the novell-lum code.
Comment 4 Thomas Biege 2006-03-15 18:09:44 UTC
./config/confuncs.c:    ret = system("sed /pam_ndssso/d /etc/pam.conf | sed s/pam_nds/pam_nam/ >/tmp/pam.conf.up; cp -f /tmp/pam.conf.up /etc/pam.conf");

./config/confuncs.c:    //ret = system("cd /etc/pam.d; for pfile in `ls /etc/pam.d`; do sed /pam_ndssso/d $pfile| sed s/pam_nds/pam_nam/ > /tmp/pam.up; cp -f /tmp/pam.up /etc/pam.d/$pfile ; done; cd -");

./config/confuncs.c:    ret = system("cd /etc/pam.d ; for pfile in `ls /etc/pam.d`; do if [ -f $pfile ]; then sed /pam_ndssso/d $pfile| sed s/pam_nds/pam_nam/ > /tmp/pam.up; cp -f /tmp/pam.up /etc/pam.d/$pfile; fi; done ; cd -");

./config/confuncs.c:    ret = system("sed s/nds/nam/ /etc/nsswitch.conf > /tmp/nsswitch.conf.up; cp -f /tmp/nsswitch.conf.up /etc/nsswitch.conf");

Not even bad programming stlye but also a security risc.
Comment 5 Alexander Danoyan 2006-03-15 19:18:11 UTC
Can you please elaborate on comment #3 ?
The code you mentioned in in the comment #4 will never be executed in Code 10.
This code provides upgrade from old Novell Account Manager product to LUM.
Comment 6 Alexander Danoyan 2006-03-15 19:34:08 UTC
adding myself to CC list
Comment 7 Alexander Danoyan 2006-03-15 22:26:40 UTC
Also keep in mind that we are not distributing the source code for novell-lum yet.
Comment 8 Thomas Biege 2006-03-16 07:08:37 UTC
comment #5:

- I assume you mean comment #2. The functions very likely lead to security problems like using strcpy(namConfigFile, optarg) will overflow the stack/heap and can be used to execute code. I did not check for exploitability because there are just too much of them. The counting of these functions is just to get a quick overview of the code. Please use saver replacements that do check for the size of the destination buffer (strncpy(), strncat(), snprintf()), do NOT use system() or pipe(), and do NOT write
to /tmp blindly

- If the code will never be used then remove it please. It's a potential security risk.

Comment 9 Thomas Biege 2006-03-16 08:09:18 UTC
The usage of system() to copy, remove, or rename files/dirs can be found in various code places. This is prone to security problems. Please look at other tools like pwdutils to see how to avoid calling system() and get the same tasks done faster and more secure.
Comment 10 Thomas Biege 2006-03-16 08:20:38 UTC
Note: I will add my findings subsequently to this bug entry and not write them up in a report to get this bugs handled faster.

CHECK_LENGTH(len, size)
len is of type size_t
size is of type int (unsigned?)

On 64bit machines size_t is 8 byte and int is still 4 byte. This should be kept in mind to make SIZE256 (and a like) of tye (size_t) to improve portability between different architectures.

#define SIZE256 ((size_t)  256)

Comparing and assigning different integer types can lead to security problems... but a quick check did not reveal a problem for this case.
Comment 11 Alexander Danoyan 2006-03-16 17:33:32 UTC
Ok, I already started replacing strcpy, strcat, sprintf with strncpy, strncat, snprintf. The system() calls you are reffering to can be invoked only with 
two conditions:

1. The older version of this product has to be installed on the system, which will never be Code 10 based :-)
2. User has to provide Administrative password for Administrator in the LDAP tree.
The last one means that this person has to be member of IS&T.

Also the source code you are looking also includes the set of test utilities. Executables for which we do not ship with runtime RPM.

Since there are a lot of changes need to be done with replacing strcpy, strcat, sprintf with strncpy, strncat, snprintf this may effect stability of code.

1. Are you OK if I checkin these changes post Code 10 ? 
2. If not, I would like to make these changes first in daemon and library code since they are more volnurable and if time permit make this changes 
in the utilities  ? Is it OK with you guys ?

Thanks.
Comment 12 Thomas Biege 2006-03-17 10:07:06 UTC
Please keep in mind that strn*() functions do not necessarily null-terminate strings.

- wrt. 1.
So, what is the code used for? On which plattform?

- wrt. 2.
It doesn't matter. Using system() spawns a shell and this opens a bunch of security
problems (Like not using full pathnames for tools, writing to /tmp (i.e. /tmp/unix2edir_tmp2),
unexpected changing of the environment of operation, unknown tool options (like using
a path "-rf .." when calling rm and so on)).

And again it is a big overhead.

For example:
sprintf( sysbuffer, "%s %s %s", "ps auxw | grep ", dmn, " | grep -q -v grep");

Which is: ps auxw | grep dmn | grep -q -v grep

This means 4 extra processes have to be forked. (I known it will not be used under Linux, but it is a good example.)

C code
 |
+---- /bin/sh (1)
        |   |   |
        |   |   +----- grep (4)
     ps   grep (3)
     (2) 

All these shell tasks could be done cleaner, more efficient, and more secure in C code.

I assume this code is for testing if dmn runs. Use a PID file and then call kill(pid, 0) and you are done. This means three syscalls (open, read, kill) vs. 4 processes. :-)

Q1: I am not in the position to make the final decission what has to be in CODE10 and what has not to be there. But personally I would say: No, not with these bugs. (note: I just started looking at the code...)

Q2: That's perfectly ok with me. Thanks!

Comment 13 Thomas Biege 2006-03-17 10:09:06 UTC
corerction:
"This means 4 extra processes have to be forked. (I known it will not be used
under Linux, but it is a good example.)"

This example is executed.
Comment 14 Thomas Biege 2006-03-20 09:11:45 UTC
pam_sm_authenticate() function:
- char *userFDN,pass1[MB_LEN_MAX * PASS_LEN]
- if (_nds_pam_read_options(argc,(const char **)argv,pass,OPTIONS) == PAM_SUCCESS)
  {
	strcpy(pass1,pass);
  }

I can't test it but it seems to be a remote root bug.
Comment 15 Thomas Biege 2006-03-20 09:54:14 UTC
pam_sm_authenticate():
- storeresult is not free'ed after a successful pam_set_data()
Comment 16 Thomas Biege 2006-03-20 09:55:29 UTC
err... or will it be free'ed during cleanup?
Comment 19 Thomas Biege 2006-03-20 15:22:43 UTC
common_passwd():
- 		if (userfdnret=_nds_getuserFDN(username,tmp,sizeof(tmp), NULL)!=PAM_SUCCESS)
		{
			if (userfdnret==PAM_USER_UNKNOWN)
			{
				sprintf(errmsg,UAM_QUOT_SQUOT_DOES_NOT_EXIST_OR_
                                QUOT_SQUOT_BELONGS_TO_A_GROUP_
                                WHICH_DOES_NOT_HAVE_ACCESS_RIGHTS_
                                TO_THE_CURRENT_WORKSTATION__OR_
                                UNABLE_TO_GET_QUOT_SQUOT_CONTEXT_
                                ,username,username,username);

This one looks like another root hole (local).
Comment 20 Alexander Danoyan 2006-03-21 00:05:06 UTC
Can you provide more details on the comment #19 ? is it just sprintf you are reffering or more ? Thanks.
Comment 21 Thomas Biege 2006-03-21 06:53:46 UTC
Just the sprintf() and the username (which can be considered as data from an untrusted source). Does everything fit into errmsg?
Comment 22 Thomas Biege 2006-03-21 09:28:26 UTC
pam_misc.c:init_sec_salt()
- The salt is constant. A constant salt does not have an effect (= making a dictionary attack inpractical).

http://en.wikipedia.org/wiki/Password_salting
Comment 23 Thomas Biege 2006-03-21 09:31:47 UTC
Use a changing value which is not related to the password (like a hash-value of the password) as salt. Something like "srand(time(NULL) ^ getpid()); rand()" should be ok for this task.
Comment 24 Thomas Biege 2006-03-21 11:42:09 UTC
pam_misc.c:cert_callback()
I see the code for parsing and adding a certificate but where is the cert verified to be genuine?
Quickliy looking into the source-code of the novell-ldapext packages does not reveal anything.
Comment 25 Thomas Biege 2006-03-21 11:44:28 UTC
additionally can cert.ength be trusted here "cert.data = (void *)malloc(cert.length); "?
Comment 26 Thomas Biege 2006-03-21 13:36:39 UTC
pam_misc.c:itoa()
- just a note: on the borders of an integer this function seems not to work correctly

> ./itoa-test -123457777777777777
integer:  -2147483648
        n = -2147483648
        n % 10 = -8 + 48 ('0')
        s = (-
        s[i] = ( <-> s[j] = -
ascii: -(

n % 10 + '0' -->  -8 + 48 = 40 --> '('

Comment 27 Thomas Biege 2006-03-21 13:51:00 UTC
Can you give me more details about the background of the following statement from the man-page of pan_nam, please?

"The server certificate signed by a Certificate Authority is created and stored in the local work
station during Novell Account Management (NAM) installation."

Does this mean the SSL server side does request a cert from the SSL client side, or do you want to verify the cert of the server?

Comment 28 Thomas Biege 2006-03-21 14:16:19 UTC
pam_misc.c:CryptPasswordForCache():
- This function also includes a constant salt.
Comment 29 Thomas Biege 2006-03-22 09:49:03 UTC
create_pam.c:mkdirp():
- strcpy() may overflow path
- the removing of trailing spaces doe not work, p_ptr already point to the end
  of the buffer which is 0
- strcat(path,"/"), will write a 0 behind the buffer if path is sizeof(path) bytes long
- check for NULL path is not needed b/c you already added a trailing '/'
- note: removing spaces from the beginning of the path can turn a relative path
  into an absolute path: "    /foo/" --> "/foo/"
  or: "        " will become "/"
- before the main while-loop you do a p_ptr++, if path is "/" this will point behind
  the buffer as far as I can see
- the "p_ptr = tmp_ptr + 1;" in the while-loop can point behind the buffer
Example:
> ./mkdirp-test "        /        "
MAIN: path is '        /        '
mkdirp:17: strcpy('        /        ')
mkdirp:20: set p_ptr to 3219801508 + 17 = 3219801525
mkdirp:22: removing trailing spaces in '        /        '
mkdirp:31: p_ptr = 3219801524, path = 3219801508
mkdirp:33: check for trailing '/' in '        /        '
mkdirp:37: added trailing '/': '        /        /'
mkdirp:40: p_ptr = path
mkdirp:43: removing leading spaces in '        /        /'
mkdirp:47: increment p_ptr to 3219801509
mkdirp:47: increment p_ptr to 3219801510
mkdirp:47: increment p_ptr to 3219801511
mkdirp:47: increment p_ptr to 3219801512
mkdirp:47: increment p_ptr to 3219801513
mkdirp:47: increment p_ptr to 3219801514
mkdirp:47: increment p_ptr to 3219801515
mkdirp:47: increment p_ptr to 3219801516
mkdirp:50: check for NULL path
mkdirp:57: check if path is absolute: '/        /'
mkdirp:65: p_ptr incremented to 3219801517, path ends at 3219801526
mkdirp:71: strchr(p_ptr, '/')
mkdirp:75: remove '/'
mkdirp:80: cur_path = '/        '
mkdirp:85: p_ptr = tmp_ptr+1 -> 3219801526, path ends at 3219801525

3219801526 > 3219801525

Another Example:
> ./mkdirp-test "        /        /     ////"
MAIN: path is '        /        /     ////'
mkdirp:17: strcpy('        /        /     ////')
mkdirp:20: set p_ptr to 3218805476 + 27 = 3218805503
mkdirp:22: removing trailing spaces in '        /        /     ////'
mkdirp:31: p_ptr = 3218805502, path = 3218805476
mkdirp:33: check for trailing '/' in '        /        /     ////'
mkdirp:40: p_ptr = path
mkdirp:43: removing leading spaces in '        /        /     ////'
mkdirp:47: increment p_ptr to 3218805477
mkdirp:47: increment p_ptr to 3218805478
mkdirp:47: increment p_ptr to 3218805479
mkdirp:47: increment p_ptr to 3218805480
mkdirp:47: increment p_ptr to 3218805481
mkdirp:47: increment p_ptr to 3218805482
mkdirp:47: increment p_ptr to 3218805483
mkdirp:47: increment p_ptr to 3218805484
mkdirp:50: check for NULL path
mkdirp:57: check if path is absolute: '/        /     ////'
mkdirp:65: p_ptr incremented to 3218805485, path ends at 3218805503
mkdirp:71: strchr(p_ptr, '/')
mkdirp:75: remove '/'
mkdirp:80: cur_path = '/        '
mkdirp:85: p_ptr = tmp_ptr+1 -> 3218805494, path ends at 3218805493
mkdirp:71: strchr(p_ptr, '/')
mkdirp:75: remove '/'
mkdirp:80: cur_path = '/        /     '
mkdirp:85: p_ptr = tmp_ptr+1 -> 3218805500, path ends at 3218805493
mkdirp:71: strchr(p_ptr, '/')
mkdirp:75: remove '/'
mkdirp:80: cur_path = '/        /     /'
mkdirp:85: p_ptr = tmp_ptr+1 -> 3218805501, path ends at 3218805493
mkdirp:71: strchr(p_ptr, '/')
mkdirp:75: remove '/'
mkdirp:80: cur_path = '/        /     //'
mkdirp:85: p_ptr = tmp_ptr+1 -> 3218805502, path ends at 3218805493
mkdirp:71: strchr(p_ptr, '/')
mkdirp:75: remove '/'
mkdirp:80: cur_path = '/        /     ///'
mkdirp:85: p_ptr = tmp_ptr+1 -> 3218805503, path ends at 3218805493


Generally spaces in pathnames are allowed and modifying/cleaning a pathname is not an easy task.


And the overflow:
>./mkdirp-test $(perl -e 'print "A" x 2000') 
[...]
mkdirp:57: check if path is absolute: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADtà¿AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/'
mkdirp:60: no absolute path
Speicherzugriffsfehler
Comment 30 Thomas Biege 2006-03-22 09:49:40 UTC
"Speicherzugriffsfehler" means SEGV :)
Comment 31 Thomas Biege 2006-03-22 09:51:11 UTC
- before the main while-loop you do a p_ptr++, if path is "/" this will point
behind the buffer as far as I can see

This is not an issue. It just points to the end.
Comment 32 Thomas Biege 2006-03-22 11:08:13 UTC
./pam done
next on the list: ./namcd
Comment 33 Thomas Biege 2006-03-22 12:34:48 UTC
NSS_UNIXSTR_PATH should not point into /etc but into /var/run/namcd or similiar.
Comment 34 Thomas Biege 2006-03-22 12:40:40 UTC
missed one comment for create_home.c:mkdirp():
- why is mkdir used with permission set 00777?
Comment 35 Thomas Biege 2006-03-22 14:00:41 UTC
namcd.c:handler_worker():

	if (signum == SIGSEGV)
	{
//		/* This is a temporary fix for daemon getting killed because 
//			 of SIGSEGV in one of the worker threads. To solve this we 
//			 are catching this signal and killing that thread smoothly 
//			 and crating one more thread
//		 */
		pthread_t self = pthread_self();

		syslog(LOG_ERR, "SIGSEGV cought in this thread: [%d], restarting daemon \n", self);

		rerunDaemon();
	}


A segmentation fault is a very critical error. The program should not restart if it catches one. It even helps local attackers who can use this behaviour for brute-forcing the right offset needed to successfully exploit a possible buffer overflow.
Comment 36 Thomas Biege 2006-03-22 14:17:39 UTC
The variable cur_signal is used in an asynchronous way, I think it is therefore better to declare it as
"volatile int cur_signal = 0;" instead of "int cur_signal = 0;"
Comment 37 Thomas Biege 2006-03-22 14:40:53 UTC
namcd.c:init_threads():

Please replace

system("touch /var/lib/novell-lum/.namcdloaded > /dev/null 2>&1");

with

open(filename, O_CREAT)
Comment 38 Marcus Meissner 2006-03-22 14:43:33 UTC
regarding #c36 ... volatile is never the right solution except in some kernel drivers. you need to use the correct pthread locking primitives.

and #c37 ... open(filename, O_CREAT, 0600); (or whatever file permission) ;)
Comment 39 Thomas Biege 2006-03-22 15:45:20 UTC
Marcus,
regarding the volatile I was thinking about the handling of signals which are async. but are queued one after another so no lock is needed for concurrent access like in threads. volatile just prevents the compiler from optimizing this variable which might have bad side-effects AFAIK.
But you are right, this variable is used within different threads and needs locking.

Nevertheless I missed one keyword to make operations atomic. "volatile sig_atomic_t ..."
Comment 40 Thomas Biege 2006-03-22 15:53:56 UTC
JFYI. When I am done with namcd I will add a priority list of bugs.
Comment 41 Thomas Biege 2006-03-22 16:18:45 UTC
packages approved. Thanks!
Comment 42 Thomas Biege 2006-03-22 16:19:09 UTC
hrm, wrong bug#
Comment 44 Thomas Biege 2006-03-23 19:49:13 UTC
One question. Is the user password handled by namcd stored as hash or as plaintext?
Comment 45 Thomas Biege 2006-03-23 20:02:25 UTC
request.c:
- often values from the socket are accepted as trusted values and will be used as arguments for other functions without checking of type, range, ... For example:
int readFromSock(int sockfd, int flag, int *len, char **name ) 
{
...
	if (_nds_nss_readFromSock(sockfd,(char *)len, sizeof(int), NORESPONSE_TIMEOUT) != sizeof(int))
         ...
	if (flag==LENANDDATA)
	{
		int nb = *len,nbr;
		*name = (char*)malloc(nb);

- the above example is just a denial-of-service bug but some code line later it looks like it can be used to overflow buffer *name

     if ((nbr = _nds_nss_readFromSock(sockfd,*name, *len, NORESPONSE_TIMEOUT)) != nb)
     {
      ...


So, don't trust values read from the socket.

Hm, can everybody call the set-function setPwdForName() and polute the cache with it?
Comment 46 Thomas Biege 2006-03-24 08:56:58 UTC
Created attachment 74859 [details]
0_SCAN.lint

Output of:

find novell-lum-2.2.0/ -name "*.c" -exec splint -warnposix +showcolumn +showsummary +stats +showfunc -namechecks +strict {} \; 2>&1 | tee 0_SCAN.lint
Comment 47 Thomas Biege 2006-03-24 09:02:29 UTC
Just added this for the sake of completeness. Analysis of the output will be added later.
Comment 48 Thomas Biege 2006-03-24 14:26:23 UTC
Created attachment 74920 [details]
0_SCAN_2.splint

for i in $(find novell-lum-2.2.0/ -name "*.c");do  echo "[ FILE: $i ]"; splint -warnposix -warnflags -preproc +forcehints +showsummary +stats -namechecks -exportconst  $i  2>/dev/null;echo "------------------------------------------------------------------------------------------";done > 0_SCAN_2.splint


Better to read and less noise.

Security-wise the clashes between the types are most interesting.
Comment 49 Thomas Biege 2006-03-27 10:14:46 UTC
namcd/cache.c:
- the return values of the *alloc() functions should be checked everytime
- there are some sprintf(str,...) calls which can overflow even if you do malloc(len) before. len is a signed integer and can theoretically wrap around twice to be positive again by large passwd entries (like the gecos field which can be controlled by users sometimes)
Comment 50 Thomas Biege 2006-03-27 10:24:53 UTC
will not review namcd/hash.c
Comment 51 Thomas Biege 2006-03-27 12:50:03 UTC
namcd/nss_ldap.c
- some sprintf(filter, ...) calls use input from untrusted sources and may therefore overflow 'filter'
- note: the keyword 'access' which is reserved for access(2) is used as jump point
- the return values of the *alloc() functions should be checked everytime
- I am still very unsure about the usage of SLL in the code, can you please explain it to me?
Comment 52 Thomas Biege 2006-03-27 13:19:29 UTC
summary of novell-lum (ordered by priority):
- check every sprintf(), strcpy, strcat() call for possible overflows
- no hardcoded passwords
- are SSL certificates verified? I need more info here.
- fix mkdirp()
- replace system() with C code
- do not trust vaues from sources you can't control (like socket input, certificates, user names, directories, hostnames, and so on) (like comment #45)
- check return values from important functions like *alloc()
- take care using /tmp
- use size_t for variables that hold sizes and length
- no constant salt values
- the split findings (will attach a truncated version in a few minutes)
- etc.
Comment 53 Thomas Biege 2006-03-27 13:29:04 UTC
Created attachment 75134 [details]
0_SCAN_2_stripped.splint
Comment 54 Thomas Biege 2006-03-27 13:30:13 UTC
Ok, novell-lum is done (just a quick check unfortunately).

TODO:
- more code checking
- runtime testing
- checking for design errors
Comment 55 Marcus Meissner 2006-03-28 16:21:30 UTC
after confering with Thomas and the rest of the team we come to the conclusion
that the points in #c52 need to be addressed before we can agree
to a Code10 inclusion of the code.
Comment 56 Alexander Danoyan 2006-03-29 05:47:37 UTC
I addressed and checked in issues for following comments:

C#14, C#17, C#19, C#27 (added more explanation), C#29, C#33, C#34, C#37, C#39, 
C#49, C#51.

Answer for C#44: namcd stores password for users as MD5 hash.

Answer for C#27: get certificate from the server. More details below.

Answer for C#51 and C#52: CA is public certificate of the LDAP server.
This certificate (via APIs) does get extracted/requested by the namconfig utility during initial install/configuration of LUM and placed into /var/lib/novell-lum/ directory. When LDAP server and client are on the same side of the same firewall this is just extra convenience. If LDAP server and the client are not located inside of the same firewall this extracted certificate must be overwritten by certificate which was delivered via secure  trusted channel. 
Comment 57 Thomas Biege 2006-03-29 16:24:15 UTC
Hm, where in the code is the signature chain of the cert verified, and where is the hostname (CN) in the cert compared with the remote peers address?
Comment 58 Thomas Biege 2006-03-29 16:27:53 UTC
Created attachment 75596 [details]
0_SCAN.splint

novell-NLDAPsdk-8.8:

This splint output lists buffer overflows, format string bugs, memory leaks, assignment of different integer types, possible null reference, and so on.
Comment 59 Thomas Biege 2006-03-29 18:24:30 UTC
./novell-NLDAPsdk-8.8/nlibraries/nldapx/ldapssl/src/ldapssl/debug.c:TraceStart()
- just a note:
336:   len = sprintf(temp, "%s tracing activated:\n"
337-                 " Start Time: %s\n"
338-                 " Version: %d.%d.%d %s\n"
--
349-                 __DATE__, __TIME__); 
350-
351:   assert(len < sizeof(temp)/sizeof(temp[0]));

detecting buffer overflows after they happened does not help.

fortunately the sprintf() parameters can't be influenced from the outside.
Comment 60 Thomas Biege 2006-03-29 18:47:39 UTC
to answer my comment #57:

./novell-NLDAPsdk-8.8/nlibraries/nldapx/ldapssl/src/ldapssl/ssl_cert.c

verify_callback() does the job together with openssl.

Comment 61 Thomas Biege 2006-03-29 19:14:37 UTC
SSL cert verification.

if openssl has a problem with verifying a cert it calls verify_callback().
verify_callback() calls cert_callback().
cert_callback() calls ldapssl_get_cert() to get a trusted root cert from eDirectory
and adds the cert using ldapssl_add_trusted_cert(). If adding the cert is successful
add_cert() propagates this success back to cert_callback() and further to 
verify_callback().

The cert, which was _invalid_, is accepted now _without_ any verifycation.

Result: SSL encryption is broken.

Is this correct?
Comment 62 Alexander Danoyan 2006-03-29 20:42:45 UTC
Thomas, you may want create a separate entry for novell-NLDAPsdk and novell-NLDAPbase audit. The manager who owns the code is Rajkumar V from India Development Center. He need to be CCed or even be assigned this new entry.
Thanks. 
Comment 63 Alexander Danoyan 2006-03-29 21:58:00 UTC
On comment #61. Please read carefully my response in c#56. Section "Answer for C#51 and C#52:"
Comment 64 Thomas Biege 2006-03-30 06:55:08 UTC
I read your explanation about SSL handling but it didn't answer my question.

1st sentence makes no sense because CA means certification authority which is a organisational entity and not a certificate
2nd sentence describes how a certificate is received via another tool (this process might be vulnerable as well)
The rest includes no information at all.

Maybe I have a wrong picture of the process in my had.

The pam_lum module wants to talk to the LDAP server using SSL. pam_lum needs to verify the LDAP server cert received. If this server cert is signed with a special CA cert then this CA cert needs to be on the client side. I think this is the process you described in comment #56.

But verfiy_callback()->cert_callback() is called whenever the cert can't be verified to be trusted. Now you add a CA cert to your X509 storage which will be used *the next time* a server cert needs to be verified.

So, the next time you receive the server cert which is signed by the CA cert you added previously  the verification will be ok.

The bad thing is that you propagate the successful addition of the CA cert back to verify_callback() which just accepts this connection _without_ further verification. This happens for every untrusted server cert no matter how often you add the CA cert.
Comment 65 Alexander Danoyan 2006-03-30 08:03:43 UTC
The code you are referring to (verify_callback() etc) will be invoked only if certificate is not found in /var/lib/novell-lum directory. The last one can occur for two reasons:

1. Administrator deleted the certificate by mistake (this is bad on his/her side).
or 
2. LDAP server and client are running in the controlled / trusted environment.
Then you may ask why even boder right? Well you still need to encrypt (the passwords on the wire for example) even though we trust the LDAP servers.

When you are referring to my 2nd sentence that the process may be vulnerable as well it is not true, unless admin is careless. The procedure to extract the certificate usually done using iManager or ConsoleOne utility from LDAP object in eDirectory. Then this certificate copied on the floppy and distributed to the LDAP clients or emailed via secure email or zipped with the password and emailed. In order to get to LDAP object in eDirectory admin has to login and also has to have enough rights to access the object. I hope this help.
Comment 66 Thomas Biege 2006-03-30 08:19:58 UTC
Hm, we are talking about different things now. (BTW I would not assume any security policy of a network you can not control.)

The point is that cert_callback() is simply used wrong.

This callback function is used whenever a certificate can not be verified.
The *purpose* of this function is to show cert details to an user and let him/her interactively verfify the cert (for example by comparing the fingerprint).
You used it to add a CA cert which you *assume* is missing. This is not just a workaround but also breaks SSL security completely.

Please read "Viega, Messier, Chandra; Network Security with OpenSSL, O'Reilly", page 130 ff.
Comment 67 Alexander Danoyan 2006-03-30 18:42:57 UTC
I think it is time for the call :-) Call me at 801-861-5839.
Comment 68 Thomas Biege 2006-03-31 14:32:44 UTC
./novell-NLDAPsdk-8.8/nldapx/ldapgss/src/gss_bind.c:ldap_gssbind()
- possible buffer overflow: strcat(service_name, ldapHost);

The exploitablity depends on the code that uses this lib call.
Comment 69 Thomas Biege 2006-03-31 14:36:09 UTC
ignore previous bug entry. it was the wrong bug.
Comment 70 Thomas Biege 2006-04-03 13:24:55 UTC
Last entry in novell-lum.changes:
-------------------------------------------------------------------
Fri Mar 31 00:13:32 CEST 2006 - adanoyan@suse.de

- adding more entries per AJ request

- replaced number of strcpy, strcat, sprintf functions with strncpy, strncat, snprintf functions
(cache.c and nss_ldap.c)
- removed code (since it is not used anyway) with hardcoded proxy password for UWS
- fixed mkdirp()
- added code to check return code from *alloc calls where it was missing
- removed /tmp directory usage except the upgrade code
- moved UDS from /etc/ location to /var/lib/novell-lum location
- replaced system(touch... with open(...

-------------------------------------------------------------------
Comment 71 Thomas Biege 2006-04-05 08:07:08 UTC
A lot of things got fixed.

I diff'ed the current package against the one I reviewed and will attach it in a few minutes.

One thing I missed above is _pam_log() in pam_ap.c can be made more bullet-proof.

static void _pam_log (int error, const char *format, ...)
{
	char buffer [1024];
	char identification [32];
	va_list args;

	sprintf (identification, "%s %s ", MODULE_NAME, VERSION);
	va_start (args, format);
	vsprintf (buffer, format, args); // XXX tom: use vsnprintf()
	openlog (identification, LOG_PID, LOG_AUTH);
	/*  openlog (identification, LOG_PID, LOG_LOCAL0); */
	setlogmask (LOG_UPTO (LOG_DEBUG));
	syslog (error, buffer); // XXX tom: format string bug: use syslog(error, "%s", buffer)
	closelog ();
}
Comment 72 Thomas Biege 2006-04-05 09:25:37 UTC
Created attachment 76619 [details]
novell-lum.diff

This are issues which are left open.

summary:
- some sprintf and strcpy's that can be replaced
- constant salt values (does changing this mean a problem with the underlying structure of eDirectory or something a like?)
- directly using untrusted values
- the _pam_log() as mentioned above
Comment 73 Alexander Danoyan 2006-04-05 16:40:43 UTC
Please specify sprintf and strcpy which need to be replaced.
Please specify places for untrusted values.
Comment 74 Thomas Biege 2006-04-05 17:04:14 UTC
search for "XXX tom"
Comment 75 Alexander Danoyan 2006-04-06 20:51:35 UTC
> Thu Apr  6 21:53:47 CEST 2006 - adanoyan@suse.de
>
> - replaced more strcpy, strcat, sprintf functions with strncpy, strncat, snprintf functions
> - removed number of system() calls
> - replaced system() call with C code
> - added full path to the parameter for the remaining system() calls
> - changed location for UDS from /var/lib/novell-lum to /var/run/novell-lum
> - added check for return code from calloc
> - added check on length of string
> - commented out call to ldapssl_set_verify_callback from pam and daemon code
> - fixed return error code in pam_sm_auth
> - commented out calls to set LM and NT passwords, note used for now
> - added checks for the range of values comming via UDS, because they are untrusted
> - fixed bug in _pam_log()
> - modified init script for daemon to use startproc and killproc utilities

Submitted into Beta10 STABLE
Comment 76 Alexander Danoyan 2006-04-06 21:02:07 UTC
TODO:

- look at copy_tree() 
- check itoa()
- look into random salt
- on the et commands over UDS pass and check UID for user to prevent hash password pollution.
Comment 77 Thomas Biege 2006-04-07 07:57:20 UTC
Thanks a lot for fixing this so fast!

I forgot about Comment #45 and setPwdForName().

Can everybody on the system use it?

If so what happens when I set the password for user 'joe' and execute "su - joe"?

Comment 78 Alexander Danoyan 2006-04-09 21:12:41 UTC
Yes, this is what I am going to do next. See my TODO list Comment #76 !
Comment 79 Thomas Biege 2006-04-10 06:38:39 UTC
I saw your todo list. :)

But if this bug is serious like this it can't wait for SP1.

Comment 80 Alexander Danoyan 2006-04-10 16:25:14 UTC
Hey Thomas, the TODO list is NOT SP1 list. :-) 
Otherwise I would call it SP1 list.
This is just reminder to myself what still needs to be done.
Do not worry !
Comment 81 Thomas Biege 2006-04-11 10:29:46 UTC
Ok, new code drop looks good.

But cert_callback() is still used:

thomas> find novell-lum-2.2.0/ -name "*.c" -exec grep --with-filename set_verify_callback {} \;

novell-lum-2.2.0/config/confuncs.c:          ret_val = ldapssl_set_verify_callback(cert_callback);
novell-lum-2.2.0/unix2edir/unix2edir.c:                      err=ldapssl_set_verify_callback(cert_callback);

The first case is used by namconfig; we made an exception here as long as we document it.
The current namconfig.1 doesn't explain this behavior (which is still an insecure workaround) to the admin.

The code in unix2edit should be removed.




Comment 82 Alexander Danoyan 2006-04-12 02:49:15 UTC
Wed Apr 12 04:40:58 CEST 2006 - adanoyan@suse.de

- added code to check on identity of the person who is trying to set hash password in the LUM cache
- commented out certcallback in unix2edir utility
- added more information in man pages for namconfig about the certificate file
Comment 83 Thomas Biege 2006-04-18 10:53:46 UTC
Code looks good now.

the following issues are left:
- don't restart on SEGV, better fix the bug ;)
- look at copy_tree() 
- check itoa()
- look into random salt
- replace system() with C-code

Alex,
do you have something else to add to the list?

Comment 84 Alexander Danoyan 2006-04-18 15:58:46 UTC
This list looks good. 
Are we OK to move this list to SP1 ?
Comment 85 Thomas Biege 2006-04-19 07:06:09 UTC
Yes, it's ok.
Comment 86 Matthias Fruehauf 2006-05-04 13:56:58 UTC
What is the status of this bug? ready to be closed?
Comment 87 Thomas Biege 2006-05-24 08:09:05 UTC
Maybe Alex needs the SP1 list... Alex?

From my side all the important bugs are fixed.
Comment 88 Alexander Danoyan 2006-05-24 22:38:53 UTC
I created a new bug #178647 to capture the work which need to be done for SP1.
I am OK to close this one.
Comment 89 Thomas Biege 2006-05-26 08:02:14 UTC
Ah, I was too quick.

The code was already released for the following distributions:
thomas@bragg:~> is_maintained novell-lum
Package is on CD sled10.i386
        Distribution: sles10-i386
        Distributionstring: SUSE-Linux-SLES-i386
        Marketing-Name: SUSE SLED 10 for x86
Package is on CD sled10.x86_64
        Distribution: sles10-x86_64
        Distributionstring: SUSE-Linux-SLES-x86-64
        Marketing-Name: SUSE SLED 10 for AMD64 and Intel EM64T
Package is on CD sles9-oes.i386
        Distribution: sles9-i386
        Distributionstring: Novell-Open-Enterprise-Server-i386
        Marketing-Name: Open Enterprise Server


Correct me if I am wrong.

At least it is true for OES which needs an update then.
Comment 90 Thomas Biege 2006-05-26 12:04:29 UTC
MaintenanceTracker-4434
Comment 91 Alexander Danoyan 2006-05-26 21:21:11 UTC
Thomas, I do not understand your question. This code already checked into OES (sles 9 based) version conrol reposotory in addition to autobuild for SLED10 and will be released as part of OES SP3.  
Comment 92 Thomas Biege 2006-05-31 09:13:31 UTC
Can you tell me when OES SP3 will be released?

BTW, I requested some CVE numbers from Mitre (cve.mitre.org). Try to use them in changelogs and update texts as a common cross-reference please.


>       + fixed several possible buffer overflows

CVE-2006-2622

>       + fixed SSL certificate handling to avoid man-in-the-middle attack

CVE-2006-2623

>       + fixed possible format string bug in PAM function

CVE-2006-2624

>       + poisoning credential cache can lead to local privilege escalation

CVE-2006-2625
Comment 93 Alexander Danoyan 2006-06-06 18:04:51 UTC
Thomas, I do not know when OES SP3 will be released. I asked a few people
who I thought could know, but they did not know either.
Comment 94 Thomas Biege 2006-06-07 11:23:30 UTC
Hm, this leaves customers open for attacks for an unknown period of time.

Are you familiar with online updates? Have a look at 
http://w3d.suse.de./Dev//Components/Packages/PackMan

Regarding the severity of the bugs and the unavailability of a SP3 release in near future an online update for all affected version should be supplied.
Comment 95 Thomas Biege 2006-06-26 10:27:31 UTC
Let's forget the online update. Changes in the code are too complex a the customers system may not work anymore.

So, let's do it for SP3. Is there something we need to take care of to submit this code to SP3? I saw novell-lum submits to 10.1 and STABLE (do we see OES9 submits here in Nbg.? *shrug*).
Comment 96 Thomas Biege 2006-09-06 10:08:36 UTC
reassigned to Alex to take care of SP3 submit..
Comment 97 Alexander Danoyan 2006-10-10 23:45:39 UTC
Alex,
My apologies, I thought I had already sent you Email stating the patch was released.  For customers, the patch is number:
11207   novell-lum      A       Security patch
This was released       9/29/2006.

It is available on the oes public channel.
Let me know if you have any other questions. 

>>> Alexander Danoyan 10/9/2006 7:32 PM >>>
Hi All,

How can I find out if this patch was placed to RC server to the world.
I checked the SWAMP server, but it does not tell me much.
It just says the status is inactive.
https://swamp.suse.de/webswamp/swamp/template/DisplayWorkflow.vm/workflowid/6032/history/1/historyType/Tasks
Thanks.

Alex.
Comment 98 Thomas Biege 2006-10-11 07:36:46 UTC
asked Steven from Mitre to make CVE-IDs public.