diff --git a/ibmca-segfault.fix.patch b/ibmca-segfault.fix.patch new file mode 100644 index 0000000..f5f0a45 --- /dev/null +++ b/ibmca-segfault.fix.patch @@ -0,0 +1,202 @@ +Index: openssl-ibmca-1.0.0-rc2/e_ibmca.c +=================================================================== +--- openssl-ibmca-1.0.0-rc2.orig/e_ibmca.c 2005-12-16 14:45:43.000000000 -0600 ++++ openssl-ibmca-1.0.0-rc2/e_ibmca.c 2007-01-14 18:03:31.000000000 -0600 +@@ -1582,133 +1582,92 @@ + } // end ibmca_sha256_cleanup + #endif // OPENSSL_NO_SHA256 + +-static int ibmca_mod_exp(BIGNUM * r, const BIGNUM * a, const BIGNUM * p, +- const BIGNUM * m, BN_CTX * ctx) ++static int ibmca_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, ++ const BIGNUM *m, BN_CTX *ctx) + { +- /* I need somewhere to store temporary serialised values for +- * use with the Ibmca API calls. A neat cheat - I'll use +- * BIGNUMs from the BN_CTX but access their arrays directly as +- * byte arrays . This way I don't have to clean anything +- * up. */ +- +- BIGNUM *argument = NULL; +- BIGNUM *result = NULL; +- BIGNUM *key = NULL; +- int to_return; ++ char *argument; ++ char *result; ++ char *key; ++ ICA_KEY_RSA_MODEXPO *pubkey; + int inLen, outLen, tmpLen; +- +- +- +- ICA_KEY_RSA_MODEXPO *publKey = NULL; + unsigned int rc; +- +- to_return = 0; /* expect failure */ ++ int to_return = 0; /* fail code set by default */ + + if (!ibmca_dso) { + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_NOT_LOADED); + goto err; + } +- /* Prepare the params */ +- BN_CTX_start(ctx); +- argument = BN_CTX_get(ctx); +- result = BN_CTX_get(ctx); +- key = BN_CTX_get(ctx); +- +- if (!argument || !result || !key) { +- IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_BN_CTX_FULL); +- goto err; +- } +- +- +- if (!bn_wexpand(argument, m->top) || !bn_wexpand(result, m->top) || +- !bn_wexpand(key, sizeof(*publKey) / BN_BYTES)) { +- IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_BN_EXPAND_FAIL); +- goto err; +- } +- +- publKey = (ICA_KEY_RSA_MODEXPO *) key->d; +- +- if (publKey == NULL) { ++ outLen = BN_num_bytes(m); ++ argument = malloc(outLen); ++ if (!argument) { ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED); + goto err; + } +- memset(publKey, 0, sizeof(ICA_KEY_RSA_MODEXPO)); +- +- publKey->keyType = CORRECT_ENDIANNESS(ME_KEY_TYPE); +- publKey->keyLength = +- CORRECT_ENDIANNESS(sizeof(ICA_KEY_RSA_MODEXPO)); +- publKey->expOffset = +- (char *) publKey->keyRecord - (char *) publKey; +- +- /* A quirk of the card: the exponent length has to be the same +- as the modulus (key) length */ +- +- outLen = BN_num_bytes(m); +- +-/* check for modulus length SAB*/ +- if (outLen > 256) { +- IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, +- IBMCA_R_MEXP_LENGTH_TO_LARGE); ++ result = malloc(outLen); ++ if (!result) { ++ free(argument); ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED); + goto err; + } +-/* check for modulus length SAB*/ +- +- +- publKey->expLength = publKey->nLength = outLen; +-/* SAB Check for underflow condition +- the size of the exponent is less than the size of the parameter +- then we have a big problem and will underflow the keyRecord +- buffer. Bad stuff could happen then +-*/ +- if (outLen < BN_num_bytes(p)) { +- IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, +- IBMCA_R_UNDERFLOW_KEYRECORD); ++ key = malloc(sizeof(*pubkey)); ++ if (!key) { ++ free(argument); ++ free(result); ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED); + goto err; + } +-/* SAB End check for underflow */ +- +- +- BN_bn2bin(p, &publKey->keyRecord[publKey->expLength - +- BN_num_bytes(p)]); +- BN_bn2bin(m, &publKey->keyRecord[publKey->expLength]); +- +- +- +- publKey->modulusBitLength = +- CORRECT_ENDIANNESS(publKey->nLength * 8); +- publKey->nOffset = +- CORRECT_ENDIANNESS(publKey->expOffset + publKey->expLength); +- +- publKey->expOffset = +- CORRECT_ENDIANNESS((char *) publKey->keyRecord - +- (char *) publKey); +- ++ pubkey = (ICA_KEY_RSA_MODEXPO *)key; ++ memset(pubkey, 0, sizeof(*pubkey)); ++ pubkey->keyType = CORRECT_ENDIANNESS(ME_KEY_TYPE); ++ pubkey->keyLength = CORRECT_ENDIANNESS(sizeof(ICA_KEY_RSA_MODEXPO)); ++ pubkey->expOffset = (char *)pubkey->keyRecord - (char *)pubkey; ++#define IBMCA_MAX_EXP_LEN 256 ++ if (outLen > IBMCA_MAX_EXP_LEN) { ++ free(argument); ++ free(result); ++ free(key); ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_MEXP_LENGTH_TO_LARGE); ++ goto err; ++ } ++ pubkey->expLength = pubkey->nLength = outLen; ++ if (outLen < BN_num_bytes(p)) { /* Key record underflow check */ ++ free(argument); ++ free(result); ++ free(key); ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_UNDERFLOW_KEYRECORD); ++ goto err; ++ } ++ BN_bn2bin(p, ++ &pubkey->keyRecord[(pubkey->expLength - BN_num_bytes(p))]); ++ BN_bn2bin(m, &pubkey->keyRecord[pubkey->expLength]); ++ pubkey->modulusBitLength = CORRECT_ENDIANNESS((pubkey->nLength * 8)); ++ pubkey->nOffset = ++ CORRECT_ENDIANNESS((pubkey->expOffset + pubkey->expLength)); ++ pubkey->expOffset = CORRECT_ENDIANNESS(((char *)pubkey->keyRecord - ++ (char *)pubkey)); + tmpLen = outLen; +- publKey->expLength = publKey->nLength = CORRECT_ENDIANNESS(tmpLen); +- +- /* Prepare the argument */ +- +- memset(argument->d, 0, outLen); +- BN_bn2bin(a, (unsigned char *) argument->d + outLen - +- BN_num_bytes(a)); +- ++ pubkey->expLength = pubkey->nLength = CORRECT_ENDIANNESS(tmpLen); ++ memset(argument, 0, outLen); ++ BN_bn2bin(a, ((unsigned char *)argument ++ + outLen ++ - BN_num_bytes(a))); + inLen = outLen; +- +- /* Perform the operation */ +- + if ((rc = p_icaRsaModExpo(ibmca_handle, inLen, +- (unsigned char *) argument->d, +- publKey, &outLen, +- (unsigned char *) result->d)) != 0) { ++ (unsigned char *)argument, ++ pubkey, &outLen, ++ (unsigned char *)result)) != 0) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED); + goto err; + } +- +- +- /* Convert the response */ +- BN_bin2bn((unsigned char *) result->d, outLen, r); ++ BN_bin2bn((unsigned char *)result, outLen, r); + to_return = 1; ++ free(argument); ++ free(result); ++ free(key); + err: +- BN_CTX_end(ctx); + return to_return; + } + diff --git a/ibmca-sw-fix.patch b/ibmca-sw-fix.patch new file mode 100644 index 0000000..2413e45 --- /dev/null +++ b/ibmca-sw-fix.patch @@ -0,0 +1,302 @@ +Index: openssl-ibmca-1.0.0-rc2/e_ibmca.c +=================================================================== +--- openssl-ibmca-1.0.0-rc2.orig/e_ibmca.c 2007-01-14 23:43:21.000000000 -0600 ++++ openssl-ibmca-1.0.0-rc2/e_ibmca.c 2007-01-14 23:43:31.000000000 -0600 +@@ -1711,95 +1711,78 @@ + const BIGNUM * dmp1, const BIGNUM * dmq1, + const BIGNUM * iqmp, BN_CTX * ctx) + { +- +- BIGNUM *argument = NULL; +- BIGNUM *result = NULL; +- BIGNUM *key = NULL; +- +- int to_return = 0; /* expect failure */ +- +- char *pkey = NULL; +- ICA_KEY_RSA_CRT *privKey = NULL; ++ char *argument; ++ char *result; ++ char *key; ++ unsigned char *pkey; ++ ICA_KEY_RSA_CRT *privkey; + int inLen, outLen; +- + int rc; + unsigned int offset, pSize, qSize; +- /* SAB New variables */ + unsigned int keyRecordSize; + unsigned int pbytes = BN_num_bytes(p); + unsigned int qbytes = BN_num_bytes(q); + unsigned int dmp1bytes = BN_num_bytes(dmp1); + unsigned int dmq1bytes = BN_num_bytes(dmq1); + unsigned int iqmpbytes = BN_num_bytes(iqmp); ++ int to_return = 0; + +- /* Prepare the params */ +- +- BN_CTX_start(ctx); +- argument = BN_CTX_get(ctx); +- result = BN_CTX_get(ctx); +- key = BN_CTX_get(ctx); +- +- if (!argument || !result || !key) { +- IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, IBMCA_R_BN_CTX_FULL); +- goto err; +- } +- +- if (!bn_wexpand(argument, p->top + q->top) || +- !bn_wexpand(result, p->top + q->top) || +- !bn_wexpand(key, sizeof(*privKey) / BN_BYTES)) { +- IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, +- IBMCA_R_BN_EXPAND_FAIL); +- goto err; +- } +- +- +- privKey = (ICA_KEY_RSA_CRT *) key->d; +- /* SAB Add check for total size in bytes of the parms does not +- * exceed the buffer space we have do this first +- */ +- keyRecordSize = +- pbytes + qbytes + dmp1bytes + dmq1bytes + iqmpbytes; +- if (keyRecordSize > sizeof(privKey->keyRecord)) { ++ argument = malloc((pbytes + qbytes)); ++ if (!argument) { ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED); ++ goto err; ++ } ++ result = malloc((pbytes + qbytes)); ++ if (!result) { ++ free(argument); ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED); ++ goto err; ++ } ++ key = malloc(sizeof(*privkey)); ++ if (!key) { ++ free(argument); ++ free(result); ++ IBMCAerr(IBMCA_F_IBMCA_MOD_EXP, IBMCA_R_REQUEST_FAILED); ++ goto err; ++ } ++ privkey = (ICA_KEY_RSA_CRT *)key; ++ keyRecordSize = (pbytes + qbytes + dmp1bytes + dmq1bytes + iqmpbytes); ++ if (keyRecordSize > sizeof(privkey->keyRecord)) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_OPERANDS_TO_LARGE); + goto err; + } +- + if ((qbytes + dmq1bytes) > 256) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_OPERANDS_TO_LARGE); + goto err; + } +- + if (pbytes + dmp1bytes > 256) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_OPERANDS_TO_LARGE); + goto err; + } +- +- /* end SAB additions */ +- +- memset(privKey, 0, sizeof(ICA_KEY_RSA_CRT)); +- privKey->keyType = CORRECT_ENDIANNESS(CRT_KEY_TYPE); +- privKey->keyLength = CORRECT_ENDIANNESS(sizeof(ICA_KEY_RSA_CRT)); +- privKey->modulusBitLength = +- CORRECT_ENDIANNESS(BN_num_bytes(q) * 2 * 8); +- +- /* +- * p,dp & qInv are 1 QWORD Larger +- */ +- privKey->pLength = CORRECT_ENDIANNESS(BN_num_bytes(p) + 8); +- privKey->qLength = CORRECT_ENDIANNESS(BN_num_bytes(q)); +- privKey->dpLength = CORRECT_ENDIANNESS(BN_num_bytes(dmp1) + 8); +- privKey->dqLength = CORRECT_ENDIANNESS(BN_num_bytes(dmq1)); +- privKey->qInvLength = CORRECT_ENDIANNESS(BN_num_bytes(iqmp) + 8); +- +- offset = (char *) privKey->keyRecord - (char *) privKey; +- +- qSize = BN_num_bytes(q); +- pSize = qSize + 8; /* 1 QWORD larger */ +- +- ++ memset(privkey, 0, sizeof(*privkey)); ++ privkey->keyType = CORRECT_ENDIANNESS(CRT_KEY_TYPE); ++ privkey->keyLength = CORRECT_ENDIANNESS(sizeof(ICA_KEY_RSA_CRT)); ++ privkey->modulusBitLength = CORRECT_ENDIANNESS(qbytes * 2 * 8); ++ privkey->pLength = CORRECT_ENDIANNESS(pbytes + 8); ++ privkey->qLength = CORRECT_ENDIANNESS(qbytes); ++ privkey->dpLength = CORRECT_ENDIANNESS(dmp1bytes + 8); ++ privkey->dqLength = CORRECT_ENDIANNESS(dmq1bytes); ++ privkey->qInvLength = CORRECT_ENDIANNESS(iqmpbytes + 8); ++ offset = ((char *)privkey->keyRecord - (char *)privkey); ++ qSize = qbytes; ++ pSize = (qSize + 8); /* 1 QWORD larger */ + /* SAB probably aittle redundant, but we'll verify that each + * of the components which make up a key record sent ot the card + * does not exceed the space that is allocated for it. this +@@ -1808,105 +1791,96 @@ + * could cause potential side affects on either the card or the + * result + */ +- + if ((pbytes > pSize) || (dmp1bytes > pSize) || + (iqmpbytes > pSize) || (qbytes > qSize) || + (dmq1bytes > qSize)) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_OPERANDS_TO_LARGE); + goto err; +- + } +- +- +- privKey->dpOffset = CORRECT_ENDIANNESS(offset); +- ++ privkey->dpOffset = CORRECT_ENDIANNESS(offset); + offset += pSize; +- privKey->dqOffset = CORRECT_ENDIANNESS(offset); +- ++ privkey->dqOffset = CORRECT_ENDIANNESS(offset); + offset += qSize; +- privKey->pOffset = CORRECT_ENDIANNESS(offset); +- ++ privkey->pOffset = CORRECT_ENDIANNESS(offset); + offset += pSize; +- privKey->qOffset = CORRECT_ENDIANNESS(offset); +- ++ privkey->qOffset = CORRECT_ENDIANNESS(offset); + offset += qSize; +- privKey->qInvOffset = CORRECT_ENDIANNESS(offset); +- +- pkey = (char *) privKey->keyRecord; +- +- +- /* SAB first check that we don;t under flow the buffer */ ++ privkey->qInvOffset = CORRECT_ENDIANNESS(offset); ++ pkey = (char *)privkey->keyRecord; + if (pSize < pbytes) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_UNDERFLOW_CONDITION); + goto err; + } +- +- /* pkey += pSize - BN_num_bytes(p); WROING this should be dmp1) */ +- pkey += pSize - BN_num_bytes(dmp1); ++ pkey += pSize - dmp1bytes; + BN_bn2bin(dmp1, pkey); +- pkey += BN_num_bytes(dmp1); /* move the pointer */ +- +- BN_bn2bin(dmq1, pkey); /* Copy over dmq1 */ +- +- pkey += qSize; /* move pointer */ +- pkey += pSize - BN_num_bytes(p); /* set up for zero padding of next field */ +- ++ pkey += dmp1bytes; ++ BN_bn2bin(dmq1, pkey); ++ pkey += qSize; ++ pkey += pSize - pbytes; /* set up for zero padding of next field */ + BN_bn2bin(p, pkey); +- pkey += BN_num_bytes(p); /* increment pointer by number of bytes moved */ +- ++ pkey += pbytes; + BN_bn2bin(q, pkey); +- pkey += qSize; /* move the pointer */ +- pkey += pSize - BN_num_bytes(iqmp); /* Adjust for padding */ ++ pkey += qSize; ++ pkey += pSize - iqmpbytes; /* Adjust for padding */ + BN_bn2bin(iqmp, pkey); +- + /* Prepare the argument and response */ +- +- outLen = CORRECT_ENDIANNESS(privKey->qLength) * 2; /* Correct endianess +- is used because the +- fields were converted ++ outLen = CORRECT_ENDIANNESS(privkey->qLength) * 2; /* Correct ++ endianess ++ is used ++ because ++ the ++ fields ++ were ++ converted + above */ +- + if (outLen > 256) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_OUTLEN_TO_LARGE); + goto err; + } +- + /* SAB check for underflow here on the argeument */ + if (outLen < BN_num_bytes(a)) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_UNDERFLOW_CONDITION); + goto err; + } +- +- BN_bn2bin(a, (unsigned char *) argument->d + outLen - +- BN_num_bytes(a)); ++ BN_bn2bin(a, ((unsigned char *)argument ++ + outLen ++ - BN_num_bytes(a))); + inLen = outLen; +- +- memset(result->d, 0, outLen); +- +- /* Perform the operation */ +- ++ memset(result, 0, outLen); + if ((rc = p_icaRsaCrt(ibmca_handle, inLen, +- (unsigned char *) argument->d, +- privKey, &outLen, +- (unsigned char *) result->d)) != 0) { ++ (unsigned char *)argument, ++ privkey, &outLen, ++ (unsigned char *)result)) != 0) { ++ free(argument); ++ free(result); ++ free(key); + IBMCAerr(IBMCA_F_IBMCA_MOD_EXP_CRT, + IBMCA_R_REQUEST_FAILED); + goto err; + } +- +- /* Convert the response */ +- +- BN_bin2bn((unsigned char *) result->d, outLen, r); ++ BN_bin2bn((unsigned char *)result, outLen, r); + to_return = 1; +- ++ free(argument); ++ free(result); ++ free(key); + err: +- BN_CTX_end(ctx); + return to_return; +- + } + + #ifndef OPENSSL_NO_DSA diff --git a/openssl-ibmca-1.0.0.rc2-memset-fix.patch b/openssl-ibmca-1.0.0.rc2-memset-fix.patch new file mode 100644 index 0000000..feb6295 --- /dev/null +++ b/openssl-ibmca-1.0.0.rc2-memset-fix.patch @@ -0,0 +1,11 @@ +diff -urpN openssl-ibmca-1.0.0-rc2/e_ibmca.c openssl-ibmca-1.0.0-rc2-memset_fix/e_ibmca.c +--- openssl-ibmca-1.0.0-rc2/e_ibmca.c 2007-01-22 17:23:20.000000000 +0100 ++++ openssl-ibmca-1.0.0-rc2-memset_fix/e_ibmca.c 2007-01-22 17:27:09.000000000 +0100 +@@ -1858,6 +1858,7 @@ static int ibmca_mod_exp_crt(BIGNUM * r, + IBMCA_R_UNDERFLOW_CONDITION); + goto err; + } ++ memset(argument, 0, pbytes + qbytes); + BN_bn2bin(a, ((unsigned char *)argument + + outLen + - BN_num_bytes(a))); diff --git a/openssl-ibmca.changes b/openssl-ibmca.changes index 664ade9..2bc03e7 100644 --- a/openssl-ibmca.changes +++ b/openssl-ibmca.changes @@ -1,5 +1,15 @@ ------------------------------------------------------------------- -Mon Jun 19 17:24:22 CEST 2006 - uli@suse.de +Tue Feb 13 12:51:00 CET 2007 - uli@suse.de + +- added fixes by IBM (bug #243801): + ibmca-segfault.fix: rewrite ibmca_mod_expto remove improper use of BIGNUM + object + ibmca-sw-fix: rewrite ibmca_mod_exp_crtto remove improper use of BIGNUM + object + openssl-ibmca-1.0.0.rc2-memset-fix.patch: fix memory initialization problem + +------------------------------------------------------------------- +Mon Jun 19 17:22:37 CEST 2006 - uli@suse.de - updated README (bug #185508) diff --git a/openssl-ibmca.spec b/openssl-ibmca.spec index c983cd2..52388f5 100644 --- a/openssl-ibmca.spec +++ b/openssl-ibmca.spec @@ -1,7 +1,7 @@ # # spec file for package openssl-ibmca (Version 1.0.0) # -# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2007 SUSE LINUX Products GmbH, Nuernberg, Germany. # This file and all modifications and additions to the pristine # package are under the same license as the package itself. # @@ -14,12 +14,15 @@ Name: openssl-ibmca BuildRequires: libica openssl-devel Summary: The IBMCA OpenSSL dynamic engine Version: 1.0.0 -Release: 7 +Release: 34 License: IBM Public License Group: Hardware/Other Source: openssl-ibmca-1.0.0-rc2.tar.bz2 Patch: ibmca-configure.patch Patch1: openssl-ibmca-README.patch +Patch2: ibmca-segfault.fix.patch +Patch3: ibmca-sw-fix.patch +Patch4: openssl-ibmca-1.0.0.rc2-memset-fix.patch URL: http://sourceforge.net/projects/opencryptoki BuildRoot: %{_tmppath}/%{name}-%{version}-build PreReq: %fillup_prereq %insserv_prereq @@ -42,6 +45,9 @@ Authors: %setup -n openssl-ibmca-1.0.0-rc2 %patch -p1 %patch1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 %build autoreconf --force --install @@ -51,7 +57,6 @@ export CPPFLAGS="$RPM_OPT_FLAGS" make %install - %makeinstall (cd $RPM_BUILD_ROOT; libtool --finish %_libdir/engines) rm ${RPM_BUILD_ROOT}%{_libdir}/engines/libibmca.la @@ -68,6 +73,13 @@ rm ${RPM_BUILD_ROOT}%{_libdir}/engines/libibmca.la %{_libdir}/engines/libibmca.* %changelog -n openssl-ibmca +* Tue Feb 13 2007 - uli@suse.de +- added fixes by IBM (bug #243801): + ibmca-segfault.fix: rewrite ibmca_mod_expto remove improper use of BIGNUM + object + ibmca-sw-fix: rewrite ibmca_mod_exp_crtto remove improper use of BIGNUM + object + openssl-ibmca-1.0.0.rc2-memset-fix.patch: fix memory initialization problem * Mon Jun 19 2006 - uli@suse.de - updated README (bug #185508) * Tue Mar 28 2006 - hare@suse.de