2 * Unix interface for ntlm_auth
4 * Copyright 2005, 2006 Kai Blin
5 * Copyright 2021 Hans Leidekker for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
32 #define WIN32_NO_STATUS
38 #include "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(ntlm
);
42 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
44 #define INITIAL_BUFFER_SIZE 200
46 static SECURITY_STATUS
read_line( struct ntlm_ctx
*ctx
, unsigned int *offset
)
52 if (!(ctx
->com_buf
= RtlAllocateHeap( GetProcessHeap(), 0, INITIAL_BUFFER_SIZE
)))
53 return SEC_E_INSUFFICIENT_MEMORY
;
54 ctx
->com_buf_size
= INITIAL_BUFFER_SIZE
;
55 ctx
->com_buf_offset
= 0;
61 if (ctx
->com_buf_offset
+ INITIAL_BUFFER_SIZE
> ctx
->com_buf_size
)
63 char *buf
= RtlReAllocateHeap( GetProcessHeap(), 0, ctx
->com_buf
, ctx
->com_buf_size
+ INITIAL_BUFFER_SIZE
);
64 if (!buf
) return SEC_E_INSUFFICIENT_MEMORY
;
65 ctx
->com_buf_size
+= INITIAL_BUFFER_SIZE
;
68 size
= read( ctx
->pipe_in
, ctx
->com_buf
+ ctx
->com_buf_offset
, ctx
->com_buf_size
- ctx
->com_buf_offset
);
69 if (size
<= 0) return SEC_E_INTERNAL_ERROR
;
71 ctx
->com_buf_offset
+= size
;
72 newline
= memchr( ctx
->com_buf
, '\n', ctx
->com_buf_offset
);
75 /* if there's a newline character, and we read more than that newline, we have to store the offset so we can
76 preserve the additional data */
77 if (newline
!= ctx
->com_buf
+ ctx
->com_buf_offset
) *offset
= (ctx
->com_buf
+ ctx
->com_buf_offset
) - (newline
+ 1);
84 static SECURITY_STATUS CDECL
ntlm_chat( struct ntlm_ctx
*ctx
, char *buf
, unsigned int buflen
, unsigned int *retlen
)
86 SECURITY_STATUS status
= SEC_E_OK
;
89 write( ctx
->pipe_out
, buf
, strlen(buf
) );
90 write( ctx
->pipe_out
, "\n", 1 );
92 if ((status
= read_line( ctx
, &offset
)) != SEC_E_OK
) return status
;
93 *retlen
= strlen( ctx
->com_buf
);
95 if (*retlen
> buflen
) return SEC_E_BUFFER_TOO_SMALL
;
96 if (*retlen
< 2) return SEC_E_ILLEGAL_MESSAGE
;
97 if (!strncmp( ctx
->com_buf
, "ERR", 3 )) return SEC_E_INVALID_TOKEN
;
99 memcpy( buf
, ctx
->com_buf
, *retlen
+ 1 );
101 if (!offset
) ctx
->com_buf_offset
= 0;
104 memmove( ctx
->com_buf
, ctx
->com_buf
+ ctx
->com_buf_offset
, offset
);
105 ctx
->com_buf_offset
= offset
;
111 static void CDECL
ntlm_cleanup( struct ntlm_ctx
*ctx
)
113 if (!ctx
|| (ctx
->mode
!= MODE_CLIENT
&& ctx
->mode
!= MODE_SERVER
)) return;
114 ctx
->mode
= MODE_INVALID
;
116 /* closing stdin will terminate ntlm_auth */
117 close( ctx
->pipe_out
);
118 close( ctx
->pipe_in
);
120 if (ctx
->pid
> 0) /* reap child */
124 ret
= waitpid( ctx
->pid
, NULL
, 0 );
125 } while (ret
< 0 && errno
== EINTR
);
128 RtlFreeHeap( GetProcessHeap(), 0, ctx
->com_buf
);
129 RtlFreeHeap( GetProcessHeap(), 0, ctx
);
132 static SECURITY_STATUS CDECL
ntlm_fork( char **argv
, struct ntlm_ctx
**ret_ctx
)
134 int pipe_in
[2], pipe_out
[2];
135 struct ntlm_ctx
*ctx
;
138 if (pipe2( pipe_in
, O_CLOEXEC
) < 0)
141 if (pipe( pipe_in
) < 0 ) return SEC_E_INTERNAL_ERROR
;
142 fcntl( pipe_in
[0], F_SETFD
, FD_CLOEXEC
);
143 fcntl( pipe_in
[1], F_SETFD
, FD_CLOEXEC
);
146 if (pipe2( pipe_out
, O_CLOEXEC
) < 0)
149 if (pipe( pipe_out
) < 0)
153 return SEC_E_INTERNAL_ERROR
;
155 fcntl( pipe_out
[0], F_SETFD
, FD_CLOEXEC
);
156 fcntl( pipe_out
[1], F_SETFD
, FD_CLOEXEC
);
159 if (!(ctx
= RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*ctx
) )))
163 close( pipe_out
[0] );
164 close( pipe_out
[1] );
165 return SEC_E_INSUFFICIENT_MEMORY
;
168 if (!(ctx
->pid
= fork())) /* child */
170 dup2( pipe_out
[0], 0 );
171 close( pipe_out
[0] );
172 close( pipe_out
[1] );
174 dup2( pipe_in
[1], 1 );
178 execvp( argv
[0], argv
);
180 write( 1, "BH\n", 3 );
185 ctx
->pipe_in
= pipe_in
[0];
187 ctx
->pipe_out
= pipe_out
[1];
188 close( pipe_out
[0] );
195 #define NTLM_AUTH_MAJOR_VERSION 3
196 #define NTLM_AUTH_MINOR_VERSION 0
197 #define NTLM_AUTH_MICRO_VERSION 25
199 static BOOL
check_version( void )
201 struct ntlm_ctx
*ctx
;
202 char *argv
[3], buf
[80];
206 argv
[0] = (char *)"ntlm_auth";
207 argv
[1] = (char *)"--version";
209 if (ntlm_fork( argv
, &ctx
) != SEC_E_OK
) return FALSE
;
211 if ((len
= read( ctx
->pipe_in
, buf
, sizeof(buf
) - 1 )) > 8)
214 int major
= 0, minor
= 0, micro
= 0;
216 if ((newline
= memchr( buf
, '\n', len
))) *newline
= 0;
219 if (sscanf( buf
, "Version %d.%d.%d", &major
, &minor
, µ
) == 3)
221 if (((major
> NTLM_AUTH_MAJOR_VERSION
) ||
222 (major
== NTLM_AUTH_MAJOR_VERSION
&& minor
> NTLM_AUTH_MINOR_VERSION
) ||
223 (major
== NTLM_AUTH_MAJOR_VERSION
&& minor
== NTLM_AUTH_MINOR_VERSION
&&
224 micro
>= NTLM_AUTH_MICRO_VERSION
)))
226 TRACE( "detected ntlm_auth version %d.%d.%d\n", major
, minor
, micro
);
232 if (!ret
) ERR_(winediag
)( "ntlm_auth was not found or is outdated. "
233 "Make sure that ntlm_auth >= %d.%d.%d is in your path. "
234 "Usually, you can find it in the winbind package of your distribution.\n",
235 NTLM_AUTH_MAJOR_VERSION
, NTLM_AUTH_MINOR_VERSION
, NTLM_AUTH_MICRO_VERSION
);
240 static const struct ntlm_funcs funcs
=
247 NTSTATUS CDECL
__wine_init_unix_lib( HMODULE module
, DWORD reason
, const void *ptr_in
, void *ptr_out
)
249 if (reason
!= DLL_PROCESS_ATTACH
) return STATUS_SUCCESS
;
250 if (!check_version()) return STATUS_DLL_NOT_FOUND
;
251 *(const struct ntlm_funcs
**)ptr_out
= &funcs
;
252 return STATUS_SUCCESS
;