From 37589cf9eb64be1b3d60c446532c06c54e8e6f30 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 09:59:42 +0200 Subject: [PATCH 01/14] Makefile: Use static versions of libssl and libcrypto --- Makefile | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index a1ab586..947c2e8 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,9 @@ INSTALL_PROGRAM = $(INSTALL) INSTALL_DATA = $(INSTALL) -m 644 CFLAGS=-O3 -Wall -Werror +LDFLAGS=-O3 #CFLAGS=-O0 -g -Wall +#LDFLAGS=-O0 all: mxshadowsrv libnss_mxshadow.so.2 test_server test_query_shadow @@ -33,12 +35,13 @@ get_shadow_line.o: get_shadow_line.c get_shadow_line.h common.h gcc $(CFLAGS) -c -fPIC -o get_shadow_line.o get_shadow_line.c libnss_mxshadow.so.2: libnss_mxshadow.c get_shadow_line.c common.h - gcc $(CFLAGS) -shared -o libnss_mxshadow.so.2 -Wl,-soname,libnss_mxshadow.so.2 -fPIC libnss_mxshadow.c get_shadow_line.c -lssl -lcrypto + gcc $(CFLAGS) $(LDFLAGS) -shared -o libnss_mxshadow.so.2 -Wl,-soname,libnss_mxshadow.so.2,-unresolved-symbols=report-all -fPIC libnss_mxshadow.c get_shadow_line.c -l:libssl.a -l:libcrypto.a -lpthread -ldl + test_server: test_server.o get_shadow_line.o - gcc $(CFLAGS) -o test_server test_server.o get_shadow_line.o -lssl -lcrypto + gcc $(LDFLAGS) -o test_server test_server.o get_shadow_line.o -l:libssl.a -l:libcrypto.a -lpthread -ldl mxshadowsrv: mxshadowsrv.o - gcc $(CFLAGS) -o mxshadowsrv mxshadowsrv.o -l:libssl.a -l:libcrypto.a -lpthread -ldl + gcc $(LDFLAGS) -o mxshadowsrv mxshadowsrv.o -l:libssl.a -l:libcrypto.a -lpthread -ldl mxshadowsrv.o: mxshadowsrv.c common.h test_server.o: test_server.c common.h get_shadow_line.h From 58974a666d9cc41ececf1848cd095d2f24b311ef Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 10:04:09 +0200 Subject: [PATCH 02/14] Makefile: Compile and link in one step --- Makefile | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 947c2e8..8ff1dee 100644 --- a/Makefile +++ b/Makefile @@ -25,9 +25,7 @@ INSTALL_PROGRAM = $(INSTALL) INSTALL_DATA = $(INSTALL) -m 644 CFLAGS=-O3 -Wall -Werror -LDFLAGS=-O3 #CFLAGS=-O0 -g -Wall -#LDFLAGS=-O0 all: mxshadowsrv libnss_mxshadow.so.2 test_server test_query_shadow @@ -35,16 +33,13 @@ get_shadow_line.o: get_shadow_line.c get_shadow_line.h common.h gcc $(CFLAGS) -c -fPIC -o get_shadow_line.o get_shadow_line.c libnss_mxshadow.so.2: libnss_mxshadow.c get_shadow_line.c common.h - gcc $(CFLAGS) $(LDFLAGS) -shared -o libnss_mxshadow.so.2 -Wl,-soname,libnss_mxshadow.so.2,-unresolved-symbols=report-all -fPIC libnss_mxshadow.c get_shadow_line.c -l:libssl.a -l:libcrypto.a -lpthread -ldl + gcc $(CFLAGS) -shared -o libnss_mxshadow.so.2 -Wl,-soname,libnss_mxshadow.so.2,-unresolved-symbols=report-all -fPIC libnss_mxshadow.c get_shadow_line.c -l:libssl.a -l:libcrypto.a -lpthread -ldl -test_server: test_server.o get_shadow_line.o - gcc $(LDFLAGS) -o test_server test_server.o get_shadow_line.o -l:libssl.a -l:libcrypto.a -lpthread -ldl +test_server: test_server.c get_shadow_line.c common.h get_shadow_line.h + gcc $(CFLAGS) -o test_server test_server.c get_shadow_line.c -l:libssl.a -l:libcrypto.a -lpthread -ldl -mxshadowsrv: mxshadowsrv.o - gcc $(LDFLAGS) -o mxshadowsrv mxshadowsrv.o -l:libssl.a -l:libcrypto.a -lpthread -ldl - -mxshadowsrv.o: mxshadowsrv.c common.h -test_server.o: test_server.c common.h get_shadow_line.h +mxshadowsrv: mxshadowsrv.c common.h + gcc $(CFLAGS) -o mxshadowsrv mxshadowsrv.c -l:libssl.a -l:libcrypto.a -lpthread -ldl clean: @rm *.o libnss_mxshadow.so.2 mxshadowsrv test_server test_query_shadow >/dev/null || true From 9063fdd797e2349f79eb0fbde24c63bfe3bfc071 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 12:22:16 +0200 Subject: [PATCH 03/14] get_shadow_line: Add alternative interface Add interface to return shadow line als malloced string. --- get_shadow_line.c | 17 +++++++++++++++++ get_shadow_line.h | 1 + 2 files changed, 18 insertions(+) diff --git a/get_shadow_line.c b/get_shadow_line.c index 2c289ab..127db41 100644 --- a/get_shadow_line.c +++ b/get_shadow_line.c @@ -125,4 +125,21 @@ int get_shadow_line_from_server(char *user, char *buf, size_t buflen) { return 0; } +#define BUFLEN_SPWD (1024) +int get_shadow_line_from_server_v2(char *user, char **line) { + char *buffer _cleanup_(free_string) = malloc(BUFLEN_SPWD); + if (buffer == NULL) + return -1; + int status = get_shadow_line_from_server(user, buffer, BUFLEN_SPWD); + if (status == -1) { + if (errno == ERANGE) { + /* we don't expect reply lines longer than BUFLEN_SPWD. If we get one, regard this as a protocol error */ + errno = EPROTO; + } + return -1; + } + *line = buffer; + buffer = NULL; + return 0; +} diff --git a/get_shadow_line.h b/get_shadow_line.h index 3942baf..f43b439 100644 --- a/get_shadow_line.h +++ b/get_shadow_line.h @@ -2,5 +2,6 @@ #define _GET_SHADOW_LINE_H int get_shadow_line_from_server(char *user, char *buf, size_t buflen); +int get_shadow_line_from_server_v2(char *user, char **line); #endif /* _GET_SHADOW_LINE_H */ From 131cb159bd30ee7e125218d6b95a972627307e02 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 12:30:36 +0200 Subject: [PATCH 04/14] test_server: Use new get_shadow_line interface --- test_server.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test_server.c b/test_server.c index 9128212..a03b7d7 100644 --- a/test_server.c +++ b/test_server.c @@ -1,4 +1,5 @@ #include +#include #include "get_shadow_line.h" @@ -7,11 +8,12 @@ int main(int argc, char **argv) { fprintf(stderr, "usage: %s username\n", argv[0]); return 1; } - char buf[1024]; - int status = get_shadow_line_from_server(argv[1], buf, sizeof(buf)); + char *line; + int status = get_shadow_line_from_server_v2(argv[1], &line); if (status == -1) return 1; - if (buf[0]) { - printf("%s\n", buf); + if (line[0]) { + printf("%s\n", line); } + free(line); } From e816fcb491819322ed9be15b0e48ae261f56da00 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 12:41:40 +0200 Subject: [PATCH 05/14] common.h: Add more __attribute__((unused)) We are going to include common.h in libnss_mxshadow.c and some more static functions woin't be used. Add "unused" function attributes. --- common.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common.h b/common.h index 259ff7a..db41a92 100644 --- a/common.h +++ b/common.h @@ -6,13 +6,13 @@ #define _cleanup_(x) __attribute__((cleanup(x))) -static void free_ssl_ctx(SSL_CTX **ctxp) { +static void __attribute__((unused)) free_ssl_ctx(SSL_CTX **ctxp) { if (*ctxp) { SSL_CTX_free(*ctxp); } } -static void free_ssl(SSL **sslp) { +static void __attribute__((unused)) free_ssl(SSL **sslp) { if (*sslp) { SSL_free(*sslp); } @@ -36,7 +36,7 @@ static void __attribute__((unused)) free_string(char **ptr) { } } -static void psslerror(char *str) { +static void __attribute__((unused)) psslerror(char *str) { fprintf(stderr, "%s\n", str); ERR_print_errors_fp(stderr); } From 0ba8f4514c8042832c078b3a3a990a558efbef91 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 12:43:01 +0200 Subject: [PATCH 06/14] libnss_mxshadow: Use new get_shadow_line interface --- libnss_mxshadow.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index 5660af6..2b2f207 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -12,6 +12,7 @@ #include #include "get_shadow_line.h" +#include "common.h" static long int atol_or_minus1(char *p) { return *p == '\0' ? -1 : atol(p); @@ -23,11 +24,20 @@ static unsigned long int atoul_or_minus1(char *p) { enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, char *buffer, size_t buflen, int *errnop) { - int status = get_shadow_line_from_server((char *)name, buffer, buflen); + char *line _cleanup_(free_string) = NULL; + + int status = get_shadow_line_from_server_v2((char *)name, &line); if (status == -1) { perror(__func__); return NSS_STATUS_UNAVAIL; } + size_t len = strlen(line); + if (buflen < len+1) { + errno = ERANGE; + return -1; + } + strcpy(buffer, line); + if (buffer[0] == '\0') return NSS_STATUS_NOTFOUND; From c33baeca91ead0876bcfdb6abc2713c70cf1ad7d Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:02:52 +0200 Subject: [PATCH 07/14] libnss_mxshadow.c: Remove perror call Remove perror() and leave error reporting to the caller. --- libnss_mxshadow.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index 2b2f207..5195020 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -28,7 +28,6 @@ enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, ch int status = get_shadow_line_from_server_v2((char *)name, &line); if (status == -1) { - perror(__func__); return NSS_STATUS_UNAVAIL; } size_t len = strlen(line); From e1f00f1bd01d7fc6b58c0bac998ced0f59a6d368 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:04:31 +0200 Subject: [PATCH 08/14] libnss_mxshadow: set errno on NSS_STATUS_NOTFOUND A table in the glibc manual indicates, that errno should be set to ENOENT for NSS_STATUS_NOTFOUND if the requested entry is not available. --- libnss_mxshadow.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index 5195020..5a048c3 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -37,8 +37,10 @@ enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, ch } strcpy(buffer, line); - if (buffer[0] == '\0') + if (buffer[0] == '\0') { + errno = ENOENT; return NSS_STATUS_NOTFOUND; + } char *p; From 6441fa8d320acf2daceafd3d7640d235428c04c6 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:07:18 +0200 Subject: [PATCH 09/14] libnss_mxshadow: Return errno in errnop, too From the glibc manual: "Before the function returns with a failure code, the implementation should store the value of the local errno variable in the variable pointed to be errnop. This is important to guarantee the module working in statically linked programs. The stored value must not be zero." So do it. --- libnss_mxshadow.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index 5a048c3..b8f2487 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -28,17 +28,20 @@ enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, ch int status = get_shadow_line_from_server_v2((char *)name, &line); if (status == -1) { + *errnop = errno; return NSS_STATUS_UNAVAIL; } size_t len = strlen(line); if (buflen < len+1) { errno = ERANGE; + *errnop = errno; return -1; } strcpy(buffer, line); if (buffer[0] == '\0') { errno = ENOENT; + *errnop = errno; return NSS_STATUS_NOTFOUND; } From 73c5ddd71ac3543eb3da8b88af4957fb5cdc0d57 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:17:09 +0200 Subject: [PATCH 10/14] libnss_mxshadow: Use sgetspent_ Use sgetspent_r instead of own parsing. --- libnss_mxshadow.c | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index b8f2487..6878fb6 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -14,14 +14,6 @@ #include "get_shadow_line.h" #include "common.h" -static long int atol_or_minus1(char *p) { - return *p == '\0' ? -1 : atol(p); -} - -static unsigned long int atoul_or_minus1(char *p) { - return *p == '\0' ? (unsigned long int) -1 : strtoul(p, NULL, 10); -} - enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, char *buffer, size_t buflen, int *errnop) { char *line _cleanup_(free_string) = NULL; @@ -31,30 +23,16 @@ enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, ch *errnop = errno; return NSS_STATUS_UNAVAIL; } - size_t len = strlen(line); - if (buflen < len+1) { - errno = ERANGE; - *errnop = errno; - return -1; - } - strcpy(buffer, line); - - if (buffer[0] == '\0') { + if (line[0] == '\0') { errno = ENOENT; *errnop = errno; return NSS_STATUS_NOTFOUND; } - - char *p; - - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_namp = p; - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_pwdp = p; - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_lstchg = atol_or_minus1(p); - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_min = atol_or_minus1(p); - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_max = atol_or_minus1(p); - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_warn = atol_or_minus1(p); - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_inact = atol_or_minus1(p); - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_expire = atol_or_minus1(p); - p = strsep(&buffer, ":"); if (p == NULL) return NSS_STATUS_NOTFOUND; spwd->sp_flag = atoul_or_minus1(p); + struct spwd *spbufp; + status = sgetspent_r(line, spwd, buffer, buflen, &spbufp); + if (status == -1) { + *errnop = errno; + return NSS_STATUS_UNAVAIL; + } return NSS_STATUS_SUCCESS; } From ad5da27455c20ab4c8d008ead534b4ea21a1e8b0 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:18:54 +0200 Subject: [PATCH 11/14] libnss_mxshadow: Cleanup includes --- libnss_mxshadow.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index 6878fb6..65df202 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -1,15 +1,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include #include "get_shadow_line.h" #include "common.h" From 1b61c0d5c61b4707455ad6c7762371380dd16559 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:23:07 +0200 Subject: [PATCH 12/14] get_shadow_line: Remove old interface --- get_shadow_line.c | 4 ++-- get_shadow_line.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/get_shadow_line.c b/get_shadow_line.c index 127db41..ad22d6b 100644 --- a/get_shadow_line.c +++ b/get_shadow_line.c @@ -76,7 +76,7 @@ static int connect_with_timeout(int sockfd, struct sockaddr *addr, socklen_t add return status; } -int get_shadow_line_from_server(char *user, char *buf, size_t buflen) { +static int _get_shadow_line_from_server(char *user, char *buf, size_t buflen) { struct sockaddr_in sockaddr; bzero(&sockaddr, sizeof(sockaddr)); @@ -131,7 +131,7 @@ int get_shadow_line_from_server_v2(char *user, char **line) { char *buffer _cleanup_(free_string) = malloc(BUFLEN_SPWD); if (buffer == NULL) return -1; - int status = get_shadow_line_from_server(user, buffer, BUFLEN_SPWD); + int status = _get_shadow_line_from_server(user, buffer, BUFLEN_SPWD); if (status == -1) { if (errno == ERANGE) { /* we don't expect reply lines longer than BUFLEN_SPWD. If we get one, regard this as a protocol error */ diff --git a/get_shadow_line.h b/get_shadow_line.h index f43b439..278e5dd 100644 --- a/get_shadow_line.h +++ b/get_shadow_line.h @@ -1,7 +1,6 @@ #ifndef _GET_SHADOW_LINE_H #define _GET_SHADOW_LINE_H -int get_shadow_line_from_server(char *user, char *buf, size_t buflen); int get_shadow_line_from_server_v2(char *user, char **line); #endif /* _GET_SHADOW_LINE_H */ From 7bba38321babdb7bba84800e56f29fcbd2d89567 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:26:22 +0200 Subject: [PATCH 13/14] global: Rename get_shadow_line_from_server_v2 to get_shadow_line --- get_shadow_line.c | 2 +- get_shadow_line.h | 2 +- libnss_mxshadow.c | 2 +- test_server.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/get_shadow_line.c b/get_shadow_line.c index ad22d6b..a05fdd1 100644 --- a/get_shadow_line.c +++ b/get_shadow_line.c @@ -127,7 +127,7 @@ static int _get_shadow_line_from_server(char *user, char *buf, size_t buflen) { #define BUFLEN_SPWD (1024) -int get_shadow_line_from_server_v2(char *user, char **line) { +int get_shadow_line(char *user, char **line) { char *buffer _cleanup_(free_string) = malloc(BUFLEN_SPWD); if (buffer == NULL) return -1; diff --git a/get_shadow_line.h b/get_shadow_line.h index 278e5dd..3c0811d 100644 --- a/get_shadow_line.h +++ b/get_shadow_line.h @@ -1,6 +1,6 @@ #ifndef _GET_SHADOW_LINE_H #define _GET_SHADOW_LINE_H -int get_shadow_line_from_server_v2(char *user, char **line); +int get_shadow_line(char *user, char **line); #endif /* _GET_SHADOW_LINE_H */ diff --git a/libnss_mxshadow.c b/libnss_mxshadow.c index 65df202..09a6e1d 100644 --- a/libnss_mxshadow.c +++ b/libnss_mxshadow.c @@ -9,7 +9,7 @@ enum nss_status _nss_mxshadow_getspnam_r(const char *name, struct spwd *spwd, ch char *line _cleanup_(free_string) = NULL; - int status = get_shadow_line_from_server_v2((char *)name, &line); + int status = get_shadow_line((char *)name, &line); if (status == -1) { *errnop = errno; return NSS_STATUS_UNAVAIL; diff --git a/test_server.c b/test_server.c index a03b7d7..7548432 100644 --- a/test_server.c +++ b/test_server.c @@ -9,7 +9,7 @@ int main(int argc, char **argv) { return 1; } char *line; - int status = get_shadow_line_from_server_v2(argv[1], &line); + int status = get_shadow_line(argv[1], &line); if (status == -1) return 1; if (line[0]) { From f04a31cd37d6314153e9a5e63ad6d601de5315b0 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Fri, 7 May 2021 13:40:18 +0200 Subject: [PATCH 14/14] get_shadow_line: Refactor Refactor _get_shadow_line_from_server into get_shadow_line. --- get_shadow_line.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/get_shadow_line.c b/get_shadow_line.c index a05fdd1..25bd55f 100644 --- a/get_shadow_line.c +++ b/get_shadow_line.c @@ -76,7 +76,9 @@ static int connect_with_timeout(int sockfd, struct sockaddr *addr, socklen_t add return status; } -static int _get_shadow_line_from_server(char *user, char *buf, size_t buflen) { +#define BUFLEN_SPWD (1024) + +int get_shadow_line(char *user, char **line) { struct sockaddr_in sockaddr; bzero(&sockaddr, sizeof(sockaddr)); @@ -112,33 +114,20 @@ static int _get_shadow_line_from_server(char *user, char *buf, size_t buflen) { return -1; } - len = ssl_read_with_timeout(ssl, sock, buf, buflen, 1000); - if (len<0) - return -1; - SSL_shutdown(ssl); - if (len == buflen) { - fprintf(stderr, "%s: buffer to small", __func__); - errno = ERANGE; // as getspent - return -1; - } - buf[len] = '\0'; - return 0; -} - -#define BUFLEN_SPWD (1024) - -int get_shadow_line(char *user, char **line) { char *buffer _cleanup_(free_string) = malloc(BUFLEN_SPWD); if (buffer == NULL) return -1; - int status = _get_shadow_line_from_server(user, buffer, BUFLEN_SPWD); - if (status == -1) { - if (errno == ERANGE) { - /* we don't expect reply lines longer than BUFLEN_SPWD. If we get one, regard this as a protocol error */ - errno = EPROTO; - } + + len = ssl_read_with_timeout(ssl, sock, buffer, BUFLEN_SPWD, 1000); + if (len<0) + return -1; + SSL_shutdown(ssl); + if (len == BUFLEN_SPWD) { + /* we don't expect reply lines longer than BUFLEN_SPWD. If we get one, regard this as a protocol error */ + errno = EPROTO; return -1; } + buffer[len] = '\0'; *line = buffer; buffer = NULL; return 0;