Winbind patch fixes broken client. Please have a look.

Hannes Schmidt mail at schmidt-net.via.t-online.de
Wed Jul 31 13:43:03 GMT 2002


As you suggested, I checked how other NSS libraries deal with this problem.
The libnss_files module in glibc doesn't leave files open like
libnss_winbind.

E.g., getpwent() (I should say its implementation in libnss_files) and
setpwent()
open /etc/passwd and endpwent() closes it. getpwnam() and getpwuid() close
it before returning.

I didn't completely understand libnss_nis, but it seems to do likewise.

I noticed another interesting thing: all the above NSS libraries set the
close-on-exec flag on the files they open. This is a good idea for two
reasons:

(A) security - see http://www.pgp.com/research/covert/advisories/028.asp.

(B) saving resources - an exec() overwrites all data, even the
shared libraries data. Consequently, the variable which holds the pipe/file
handle gets reset too and there is no way to close the file. It will get
opened
a second time. For each exec() one file handle will be wasted.

My patch fixes both problems - it sets the close-on-exec flag for the
winbindd
pipe handle and it makes sure that the handle isn't 0, 1, 2. I tested it on
my three
production servers. No problems.

diff -u -x *.o -x *.so -x *.po samba-2.2.5/source/nsswitch/wb_common.c
samba-2.2.5-modified/source/nsswitch/wb_common.c
--- samba-2.2.5/source/nsswitch/wb_common.c Wed Jun 19 03:13:44 2002
+++ samba-2.2.5-modified/source/nsswitch/wb_common.c Wed Jul 31 15:54:10
2002
@@ -102,6 +102,7 @@
  static pid_t our_pid;
  struct stat st;
  pstring path;
+ int old_fd, result, flags;

  if (our_pid != getpid()) {
   close_sock();
@@ -159,6 +160,30 @@
   return -1;
  }

+ /* Make sure socket handle isn't stdin, stdout or stderr */
+
+ if (winbindd_fd < 3) {
+  old_fd = winbindd_fd;
+  if ((winbindd_fd = fcntl(winbindd_fd, F_DUPFD, 3)) == -1) {
+   winbindd_fd = old_fd;
+   close_sock();
+   return -1;
+  }
+  close( old_fd );
+ }
+
+ /* Socket should be closed on exec() */
+
+ result = flags = fcntl(winbindd_fd, F_GETFD, 0);
+ if (flags >= 0) {
+  flags != FD_CLOEXEC;
+  result = fcntl( winbindd_fd, F_SETFD, flags );
+ }
+ if (result < 0) {
+  close_sock();
+  return -1;
+ }
+
  if (connect(winbindd_fd, (struct sockaddr *)&sunaddr,
       sizeof(sunaddr)) == -1) {
   close_sock();
diff -u -x *.o -x *.so -x *.po
samba-2.2.5/source/nsswitch/winbind_nss_config.h
samba-2.2.5-modified/source/nsswitch/winbind_nss_config.h
--- samba-2.2.5/source/nsswitch/winbind_nss_config.h Wed Jun 19 03:13:44
2002
+++ samba-2.2.5-modified/source/nsswitch/winbind_nss_config.h Wed Jul 31
15:21:25 2002
@@ -62,6 +62,10 @@
 #include <string.h>
 #endif

+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <errno.h>


Andrew Bartlett wrote:
> Hannes Schmidt wrote:
>>
>> Even if this is complete crap, I would appreciate any feedback
>> saying so.
>
> Firstly, don't expect instant responses - particularly over the
> weekend...
>
>> A couple of days ago I posted a problem report to the samba list
>> with some diagnostics. I seem to have found a
>> solution on my own and provide a patch here. The problem is related
>> to the fact that the winbind client code in
>> libnss_winbind.so leaves the handle to the winbind pipe open, for
>> obvious reasons. This is perfectly ok, but some
>> programs get confused if this handle is 0,1 or 2, which they expect
>> to be a handle to a different file. The patch
>> makes the winbind client (libnss_winbind.so) code more robust.
>>
>> Consider the following fragment of broken application code.
>>
>> if( fork() == 0 ) {
>>     /* child */
>>     close( 0 );             /* next open() will return 0 (stdin) */
>>     getpwent(...);        /* pid changed, so wb_common.c will
>> re-open the pipe to winbindd, the handle will be 0 */
>>     fd = open( ... );     /* returned fd is *not* 0 although the
>> code below assumes this */
>>     execve( ... );         /* executed program reads from stdin
>> which - unintentionally - is the winbind pipe */
>> } else {
>>     /* parent */
>>     ...
>> }
>>
>> Obviously, this code is broken and could be easily fixed by doing the
>> getpwent() before the close(). Unfortunately
>> the version of cron installed on my system is broken in a more
>> complex but similar way. There is probably more
>> broken code like this in other standard unix software, especially
>> since it's such a subtle mistake.
>
> It is rather - well spotted for figuring it out.
>
> How do other nss libraries deal with this?  NIS and LDAP both open
> sockets - find out if there is a standard solution to the problem.
> Personally, I don't like the idea of messing with a program's file
> descriptors - but this is a problem we will have to cope with.
>
>> The provided patch to wb_common.c makes sure that the handle to the
>> winbindd pipe is not 0, 1 or 2.
>
> For future reference, patches should be in 'diff -u' format, if
> possible.
>
>> *** /root/samba-2.2.5/source/nsswitch/wb_common.c Wed Jun 19
>> 03:13:44 2002 --- wb_common.c Thu Jul 25 16:36:39 2002
>> ***************
>> *** 94,99 ****
>> --- 94,120 ----
>>    }
>>   }
>>
>> + /* Returns a duplicate of the given file descriptor that is
>> guranteed +    not to be 0, 1 or 2 (stdin, stdout or stderr
>> respectively), unless +    an error occurs. If the given fd is
>> invalid, it will be returned +    unchanged. If a different error
>> occurs, the returned file handle +    will still be valid but it may
>> be 0, 1 or 2. If a valid file +    handle is returned and the
>> returned handle is different to the +    original one, the original
>> one will be closed. If dup() is broken, +    this function might
>> never return. */
>
> I don't like that idea.  It should fail - infinite loops are bad...
>
>> + int make_nonstd_fd(int fd)
>> + {
>> +  int saved_fd;
>> +  if (0 >= fd && fd <= 2) {
>> +      fd = dup(saved_fd = fd);
>> +      if (fd == -1) return saved_fd;
>> +      fd = make_nonstd_fd(fd);
>> +      close(saved_fd);
>> +  }
>> +  return fd;
>> + }
>> +
>>   /* Connect to winbindd socket */
>>
>>   int winbind_open_pipe_sock(void)
>> ***************
>> *** 158,164 ****
>>    if ((winbindd_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
>>     return -1;
>>    }
>> !
>>    if (connect(winbindd_fd, (struct sockaddr *)&sunaddr,
>>         sizeof(sunaddr)) == -1) {
>>     close_sock();
>> --- 179,188 ----
>>    if ((winbindd_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
>>     return -1;
>>    }
>> !
>> !  winbindd_fd = make_nonstd_fd( winbindd_fd );
>> !
>> !
>>    if (connect(winbindd_fd, (struct sockaddr *)&sunaddr,
>>         sizeof(sunaddr)) == -1) {
>>     close_sock();
>> ***************
>> *** 167,172 ****
>> --- 191,197 ----
>>
>>    return winbindd_fd;
>>   }
>> +
>>
>>   /* Write data to winbindd socket */





More information about the samba-technical mailing list