netapi32: Fix off-by-one errors in ACCESS_QueryAdminDisplayInformation and ACCESS_Que...
[wine] / dlls / netapi32 / access.c
1 /*
2  * Copyright 2002 Andriy Palamarchuk
3  *
4  * netapi32 access functions
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "ntstatus.h"
24 #define WIN32_NO_STATUS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "lmcons.h"
29 #include "lmaccess.h"
30 #include "lmapibuf.h"
31 #include "lmerr.h"
32 #include "lmuse.h"
33 #include "ntsecapi.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36 #include "wine/list.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(netapi32);
39
40 /* NOTE: So far, this is implemented to support tests that require user logins,
41  *       but not designed to handle real user databases. Those should probably
42  *       be synced with either the host's user database or with Samba.
43  *
44  * FIXME: The user database should hold all the information the USER_INFO_4 struct
45  * needs, but for the first try, I will just implement the USER_INFO_1 fields.
46  */
47
48 struct sam_user
49 {
50     struct list entry;
51     WCHAR user_name[LM20_UNLEN+1];
52     WCHAR user_password[PWLEN + 1];
53     DWORD sec_since_passwd_change;
54     DWORD user_priv;
55     LPWSTR home_dir;
56     LPWSTR user_comment;
57     DWORD user_flags;
58     LPWSTR user_logon_script_path;
59 };
60
61 static struct list user_list = LIST_INIT( user_list );
62
63 BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName);
64
65 /************************************************************
66  *                NETAPI_ValidateServername
67  *
68  * Validates server name
69  */
70 static NET_API_STATUS NETAPI_ValidateServername(LPCWSTR ServerName)
71 {
72     if (ServerName)
73     {
74         if (ServerName[0] == 0)
75             return ERROR_BAD_NETPATH;
76         else if (
77             ((ServerName[0] == '\\') &&
78              (ServerName[1] != '\\'))
79             ||
80             ((ServerName[0] == '\\') &&
81              (ServerName[1] == '\\') &&
82              (ServerName[2] == 0))
83             )
84             return ERROR_INVALID_NAME;
85     }
86     return NERR_Success;
87 }
88
89 /************************************************************
90  *                NETAPI_FindUser
91  *
92  * Looks for a user in the user database.
93  * Returns a pointer to the entry in the user list when the user
94  * is found, NULL otherwise.
95  */
96 static struct sam_user* NETAPI_FindUser(LPCWSTR UserName)
97 {
98     struct sam_user *user;
99
100     LIST_FOR_EACH_ENTRY(user, &user_list, struct sam_user, entry)
101     {
102         if(lstrcmpW(user->user_name, UserName) == 0)
103             return user;
104     }
105     return NULL;
106 }
107
108 static BOOL NETAPI_IsCurrentUser(LPCWSTR username)
109 {
110     LPWSTR curr_user = NULL;
111     DWORD dwSize;
112     BOOL ret = FALSE;
113
114     dwSize = LM20_UNLEN+1;
115     curr_user = HeapAlloc(GetProcessHeap(), 0, dwSize * sizeof(WCHAR));
116     if(!curr_user)
117     {
118         ERR("Failed to allocate memory for user name.\n");
119         goto end;
120     }
121     if(!GetUserNameW(curr_user, &dwSize))
122     {
123         ERR("Failed to get current user's user name.\n");
124         goto end;
125     }
126     if (!lstrcmpW(curr_user, username))
127     {
128         ret = TRUE;
129     }
130
131 end:
132     HeapFree(GetProcessHeap(), 0, curr_user);
133     return ret;
134 }
135
136 /************************************************************
137  *                NetUserAdd (NETAPI32.@)
138  */
139 NET_API_STATUS WINAPI NetUserAdd(LPCWSTR servername,
140                   DWORD level, LPBYTE bufptr, LPDWORD parm_err)
141 {
142     NET_API_STATUS status;
143     struct sam_user * su = NULL;
144
145     FIXME("(%s, %d, %p, %p) stub!\n", debugstr_w(servername), level, bufptr, parm_err);
146
147     if((status = NETAPI_ValidateServername(servername)) != NERR_Success)
148         return status;
149
150     switch(level)
151     {
152     /* Level 3 and 4 are identical for the purposes of NetUserAdd */
153     case 4:
154     case 3:
155         FIXME("Level 3 and 4 not implemented.\n");
156         /* Fall through */
157     case 2:
158         FIXME("Level 2 not implemented.\n");
159         /* Fall through */
160     case 1:
161     {
162         PUSER_INFO_1 ui = (PUSER_INFO_1) bufptr;
163         su = HeapAlloc(GetProcessHeap(), 0, sizeof(struct sam_user));
164         if(!su)
165         {
166             status = NERR_InternalError;
167             break;
168         }
169
170         if(lstrlenW(ui->usri1_name) > LM20_UNLEN)
171         {
172             status = NERR_BadUsername;
173             break;
174         }
175
176         /*FIXME: do other checks for a valid username */
177         lstrcpyW(su->user_name, ui->usri1_name);
178
179         if(lstrlenW(ui->usri1_password) > PWLEN)
180         {
181             /* Always return PasswordTooShort on invalid passwords. */
182             status = NERR_PasswordTooShort;
183             break;
184         }
185         lstrcpyW(su->user_password, ui->usri1_password);
186
187         su->sec_since_passwd_change = ui->usri1_password_age;
188         su->user_priv = ui->usri1_priv;
189         su->user_flags = ui->usri1_flags;
190
191         /*FIXME: set the other LPWSTRs to NULL for now */
192         su->home_dir = NULL;
193         su->user_comment = NULL;
194         su->user_logon_script_path = NULL;
195
196         list_add_head(&user_list, &su->entry);
197         return NERR_Success;
198     }
199     default:
200         TRACE("Invalid level %d specified.\n", level);
201         status = ERROR_INVALID_LEVEL;
202         break;
203     }
204
205     HeapFree(GetProcessHeap(), 0, su);
206
207     return status;
208 }
209
210 /************************************************************
211  *                NetUserDel  (NETAPI32.@)
212  */
213 NET_API_STATUS WINAPI NetUserDel(LPCWSTR servername, LPCWSTR username)
214 {
215     NET_API_STATUS status;
216     struct sam_user *user;
217
218     TRACE("(%s, %s)\n", debugstr_w(servername), debugstr_w(username));
219
220     if((status = NETAPI_ValidateServername(servername))!= NERR_Success)
221         return status;
222
223     if ((user = NETAPI_FindUser(username)) == NULL)
224         return NERR_UserNotFound;
225
226     list_remove(&user->entry);
227
228     HeapFree(GetProcessHeap(), 0, user->home_dir);
229     HeapFree(GetProcessHeap(), 0, user->user_comment);
230     HeapFree(GetProcessHeap(), 0, user->user_logon_script_path);
231     HeapFree(GetProcessHeap(), 0, user);
232
233     return NERR_Success;
234 }
235
236 /************************************************************
237  *                NetUserGetInfo  (NETAPI32.@)
238  */
239 NET_API_STATUS WINAPI
240 NetUserGetInfo(LPCWSTR servername, LPCWSTR username, DWORD level,
241                LPBYTE* bufptr)
242 {
243     NET_API_STATUS status;
244     TRACE("(%s, %s, %d, %p)\n", debugstr_w(servername), debugstr_w(username),
245           level, bufptr);
246     status = NETAPI_ValidateServername(servername);
247     if (status != NERR_Success)
248         return status;
249
250     if(!NETAPI_IsLocalComputer(servername))
251     {
252         FIXME("Only implemented for local computer, but remote server"
253               "%s was requested.\n", debugstr_w(servername));
254         return NERR_InvalidComputer;
255     }
256
257     if(!NETAPI_FindUser(username) && !NETAPI_IsCurrentUser(username))
258     {
259         TRACE("User %s is unknown.\n", debugstr_w(username));
260         return NERR_UserNotFound;
261     }
262
263     switch (level)
264     {
265     case 0:
266     {
267         PUSER_INFO_0 ui;
268         int name_sz;
269
270         name_sz = lstrlenW(username) + 1;
271
272         /* set up buffer */
273         NetApiBufferAllocate(sizeof(USER_INFO_0) + name_sz * sizeof(WCHAR),
274                              (LPVOID *) bufptr);
275
276         ui = (PUSER_INFO_0) *bufptr;
277         ui->usri0_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_0));
278
279         /* get data */
280         lstrcpyW(ui->usri0_name, username);
281         break;
282     }
283
284     case 10:
285     {
286         PUSER_INFO_10 ui;
287         PUSER_INFO_0 ui0;
288         NET_API_STATUS status;
289         /* sizes of the field buffers in WCHARS */
290         int name_sz, comment_sz, usr_comment_sz, full_name_sz;
291
292         comment_sz = 1;
293         usr_comment_sz = 1;
294         full_name_sz = 1;
295
296         /* get data */
297         status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
298         if (status != NERR_Success)
299         {
300             NetApiBufferFree(ui0);
301             return status;
302         }
303         name_sz = lstrlenW(ui0->usri0_name) + 1;
304
305         /* set up buffer */
306         NetApiBufferAllocate(sizeof(USER_INFO_10) +
307                              (name_sz + comment_sz + usr_comment_sz +
308                               full_name_sz) * sizeof(WCHAR),
309                              (LPVOID *) bufptr);
310         ui = (PUSER_INFO_10) *bufptr;
311         ui->usri10_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_10));
312         ui->usri10_comment = (LPWSTR) (
313             ((PBYTE) ui->usri10_name) + name_sz * sizeof(WCHAR));
314         ui->usri10_usr_comment = (LPWSTR) (
315             ((PBYTE) ui->usri10_comment) + comment_sz * sizeof(WCHAR));
316         ui->usri10_full_name = (LPWSTR) (
317             ((PBYTE) ui->usri10_usr_comment) + usr_comment_sz * sizeof(WCHAR));
318
319         /* set data */
320         lstrcpyW(ui->usri10_name, ui0->usri0_name);
321         NetApiBufferFree(ui0);
322         ui->usri10_comment[0] = 0;
323         ui->usri10_usr_comment[0] = 0;
324         ui->usri10_full_name[0] = 0;
325         break;
326     }
327
328     case 1:
329       {
330         static const WCHAR homedirW[] = {'H','O','M','E',0};
331         PUSER_INFO_1 ui;
332         PUSER_INFO_0 ui0;
333         NET_API_STATUS status;
334         /* sizes of the field buffers in WCHARS */
335         int name_sz, password_sz, home_dir_sz, comment_sz, script_path_sz;
336
337         password_sz = 1; /* not filled out for security reasons for NetUserGetInfo*/
338         comment_sz = 1;
339         script_path_sz = 1;
340
341        /* get data */
342         status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0);
343         if (status != NERR_Success)
344         {
345             NetApiBufferFree(ui0);
346             return status;
347         }
348         name_sz = lstrlenW(ui0->usri0_name) + 1;
349         home_dir_sz = GetEnvironmentVariableW(homedirW, NULL,0);
350         /* set up buffer */
351         NetApiBufferAllocate(sizeof(USER_INFO_1) +
352                              (name_sz + password_sz + home_dir_sz +
353                               comment_sz + script_path_sz) * sizeof(WCHAR),
354                              (LPVOID *) bufptr);
355
356         ui = (PUSER_INFO_1) *bufptr;
357         ui->usri1_name = (LPWSTR) (ui + 1);
358         ui->usri1_password = ui->usri1_name + name_sz;
359         ui->usri1_home_dir = ui->usri1_password + password_sz;
360         ui->usri1_comment = ui->usri1_home_dir + home_dir_sz;
361         ui->usri1_script_path = ui->usri1_comment + comment_sz;
362         /* set data */
363         lstrcpyW(ui->usri1_name, ui0->usri0_name);
364         NetApiBufferFree(ui0);
365         ui->usri1_password[0] = 0;
366         ui->usri1_password_age = 0;
367         ui->usri1_priv = 0;
368         GetEnvironmentVariableW(homedirW, ui->usri1_home_dir,home_dir_sz);
369         ui->usri1_comment[0] = 0;
370         ui->usri1_flags = 0;
371         ui->usri1_script_path[0] = 0;
372         break;
373       }
374     case 2:
375     case 3:
376     case 4:
377     case 11:
378     case 20:
379     case 23:
380     case 1003:
381     case 1005:
382     case 1006:
383     case 1007:
384     case 1008:
385     case 1009:
386     case 1010:
387     case 1011:
388     case 1012:
389     case 1013:
390     case 1014:
391     case 1017:
392     case 1018:
393     case 1020:
394     case 1023:
395     case 1024:
396     case 1025:
397     case 1051:
398     case 1052:
399     case 1053:
400     {
401         FIXME("Level %d is not implemented\n", level);
402         return NERR_InternalError;
403     }
404     default:
405         TRACE("Invalid level %d is specified\n", level);
406         return ERROR_INVALID_LEVEL;
407     }
408     return NERR_Success;
409 }
410
411 /************************************************************
412  *                NetUserGetLocalGroups  (NETAPI32.@)
413  */
414 NET_API_STATUS WINAPI
415 NetUserGetLocalGroups(LPCWSTR servername, LPCWSTR username, DWORD level,
416                       DWORD flags, LPBYTE* bufptr, DWORD prefmaxlen,
417                       LPDWORD entriesread, LPDWORD totalentries)
418 {
419     NET_API_STATUS status;
420     const WCHAR admins[] = {'A','d','m','i','n','i','s','t','r','a','t','o','r','s',0};
421     LPWSTR currentuser;
422     LOCALGROUP_USERS_INFO_0* info;
423     DWORD size;
424
425     FIXME("(%s, %s, %d, %08x, %p %d, %p, %p) stub!\n",
426           debugstr_w(servername), debugstr_w(username), level, flags, bufptr,
427           prefmaxlen, entriesread, totalentries);
428
429     status = NETAPI_ValidateServername(servername);
430     if (status != NERR_Success)
431         return status;
432
433     size = UNLEN + 1;
434     NetApiBufferAllocate(size * sizeof(WCHAR), (LPVOID*)&currentuser);
435     GetUserNameW(currentuser, &size);
436
437     if (lstrcmpiW(username, currentuser) && NETAPI_FindUser(username))
438     {
439         NetApiBufferFree(currentuser);
440         return NERR_UserNotFound;
441     }
442
443     NetApiBufferFree(currentuser);
444     *totalentries = 1;
445     size = sizeof(*info) + sizeof(admins);
446
447     if(prefmaxlen < size)
448         status = ERROR_MORE_DATA;
449     else
450         status = NetApiBufferAllocate(size, (LPVOID*)&info);
451
452     if(status != NERR_Success)
453     {
454         *bufptr = NULL;
455         *entriesread = 0;
456         return status;
457     }
458
459     info->lgrui0_name = (LPWSTR)((LPBYTE)info + sizeof(*info));
460     lstrcpyW(info->lgrui0_name, admins);
461
462     *bufptr = (LPBYTE)info;
463     *entriesread = 1;
464
465     return NERR_Success;
466 }
467
468 /************************************************************
469  *                NetUserEnum  (NETAPI32.@)
470  */
471 NET_API_STATUS WINAPI
472 NetUserEnum(LPCWSTR servername, DWORD level, DWORD filter, LPBYTE* bufptr,
473             DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries,
474             LPDWORD resume_handle)
475 {
476   FIXME("(%s,%d, 0x%d,%p,%d,%p,%p,%p) stub!\n", debugstr_w(servername), level,
477         filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
478
479   return ERROR_ACCESS_DENIED;
480 }
481
482 /************************************************************
483  *                ACCESS_QueryAdminDisplayInformation
484  *
485  *  Creates a buffer with information for the Admin User
486  */
487 static void ACCESS_QueryAdminDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
488 {
489     static const WCHAR sAdminUserName[] = {
490         'A','d','m','i','n','i','s','t','r','a','t','o','r',0};
491
492     /* sizes of the field buffers in WCHARS */
493     int name_sz, comment_sz, full_name_sz;
494     PNET_DISPLAY_USER usr;
495
496     /* set up buffer */
497     name_sz = lstrlenW(sAdminUserName) + 1;
498     comment_sz = 1;
499     full_name_sz = 1;
500     
501     *pdwSize = sizeof(NET_DISPLAY_USER);
502     *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
503     NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
504
505     usr = *buf;
506     usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
507     usr->usri1_comment = (LPWSTR) (
508         ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
509     usr->usri1_full_name = (LPWSTR) (
510         ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
511
512     /* set data */
513     lstrcpyW(usr->usri1_name, sAdminUserName);
514     usr->usri1_comment[0] = 0;
515     usr->usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
516     usr->usri1_full_name[0] = 0;
517     usr->usri1_user_id = DOMAIN_USER_RID_ADMIN;
518     usr->usri1_next_index = 0;
519 }
520
521 /************************************************************
522  *                ACCESS_QueryGuestDisplayInformation
523  *
524  *  Creates a buffer with information for the Guest User
525  */
526 static void ACCESS_QueryGuestDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize)
527 {
528     static const WCHAR sGuestUserName[] = {
529         'G','u','e','s','t',0 };
530
531     /* sizes of the field buffers in WCHARS */
532     int name_sz, comment_sz, full_name_sz;
533     PNET_DISPLAY_USER usr;
534
535     /* set up buffer */
536     name_sz = lstrlenW(sGuestUserName) + 1;
537     comment_sz = 1;
538     full_name_sz = 1;
539     
540     *pdwSize = sizeof(NET_DISPLAY_USER);
541     *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
542     NetApiBufferAllocate(*pdwSize, (LPVOID *) buf);
543
544     usr = *buf;
545     usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER));
546     usr->usri1_comment = (LPWSTR) (
547         ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR));
548     usr->usri1_full_name = (LPWSTR) (
549         ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR));
550
551     /* set data */
552     lstrcpyW(usr->usri1_name, sGuestUserName);
553     usr->usri1_comment[0] = 0;
554     usr->usri1_flags = UF_ACCOUNTDISABLE | UF_SCRIPT | UF_NORMAL_ACCOUNT |
555         UF_DONT_EXPIRE_PASSWD;
556     usr->usri1_full_name[0] = 0;
557     usr->usri1_user_id = DOMAIN_USER_RID_GUEST;
558     usr->usri1_next_index = 0;
559 }
560
561 /************************************************************
562  * Copies NET_DISPLAY_USER record.
563  */
564 static void ACCESS_CopyDisplayUser(const NET_DISPLAY_USER *dest, LPWSTR *dest_buf,
565                             PNET_DISPLAY_USER src)
566 {
567     LPWSTR str = *dest_buf;
568
569     src->usri1_name = str;
570     lstrcpyW(src->usri1_name, dest->usri1_name);
571     str = (LPWSTR) (
572         ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
573
574     src->usri1_comment = str;
575     lstrcpyW(src->usri1_comment, dest->usri1_comment);
576     str = (LPWSTR) (
577         ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
578
579     src->usri1_flags = dest->usri1_flags;
580
581     src->usri1_full_name = str;
582     lstrcpyW(src->usri1_full_name, dest->usri1_full_name);
583     str = (LPWSTR) (
584         ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR));
585
586     src->usri1_user_id = dest->usri1_user_id;
587     src->usri1_next_index = dest->usri1_next_index;
588     *dest_buf = str;
589 }
590
591 /************************************************************
592  *                NetQueryDisplayInformation  (NETAPI32.@)
593  *
594  * The buffer structure:
595  * - array of fixed size record of the level type
596  * - strings, referenced by the record of the level type
597  */
598 NET_API_STATUS WINAPI
599 NetQueryDisplayInformation(
600     LPCWSTR ServerName, DWORD Level, DWORD Index, DWORD EntriesRequested,
601     DWORD PreferredMaximumLength, LPDWORD ReturnedEntryCount,
602     PVOID *SortedBuffer)
603 {
604     TRACE("(%s, %d, %d, %d, %d, %p, %p)\n", debugstr_w(ServerName),
605           Level, Index, EntriesRequested, PreferredMaximumLength,
606           ReturnedEntryCount, SortedBuffer);
607
608     if(!NETAPI_IsLocalComputer(ServerName))
609     {
610         FIXME("Only implemented on local computer, but requested for "
611               "remote server %s\n", debugstr_w(ServerName));
612         return ERROR_ACCESS_DENIED;
613     }
614
615     switch (Level)
616     {
617     case 1:
618     {
619         /* current record */
620         PNET_DISPLAY_USER inf;
621         /* current available strings buffer */
622         LPWSTR str;
623         PNET_DISPLAY_USER admin, guest;
624         DWORD admin_size, guest_size;
625         LPWSTR name = NULL;
626         DWORD dwSize;
627
628         /* sizes of the field buffers in WCHARS */
629         int name_sz, comment_sz, full_name_sz;
630
631         /* number of the records, returned in SortedBuffer
632            3 - for current user, Administrator and Guest users
633          */
634         int records = 3;
635
636         FIXME("Level %d partially implemented\n", Level);
637         *ReturnedEntryCount = records;
638         comment_sz = 1;
639         full_name_sz = 1;
640
641         /* get data */
642         dwSize = UNLEN + 1;
643         NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &name);
644         if (!GetUserNameW(name, &dwSize))
645         {
646             NetApiBufferFree(name);
647             return ERROR_ACCESS_DENIED;
648         }
649         name_sz = dwSize;
650         ACCESS_QueryAdminDisplayInformation(&admin, &admin_size);
651         ACCESS_QueryGuestDisplayInformation(&guest, &guest_size);
652
653         /* set up buffer */
654         dwSize = sizeof(NET_DISPLAY_USER) * records;
655         dwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR);
656
657         NetApiBufferAllocate(dwSize +
658                              admin_size - sizeof(NET_DISPLAY_USER) +
659                              guest_size - sizeof(NET_DISPLAY_USER),
660                              SortedBuffer);
661         inf = *SortedBuffer;
662         str = (LPWSTR) ((PBYTE) inf + sizeof(NET_DISPLAY_USER) * records);
663         inf->usri1_name = str;
664         str = (LPWSTR) (
665             ((PBYTE) str) + name_sz * sizeof(WCHAR));
666         inf->usri1_comment = str;
667         str = (LPWSTR) (
668             ((PBYTE) str) + comment_sz * sizeof(WCHAR));
669         inf->usri1_full_name = str;
670         str = (LPWSTR) (
671             ((PBYTE) str) + full_name_sz * sizeof(WCHAR));
672
673         /* set data */
674         lstrcpyW(inf->usri1_name, name);
675         NetApiBufferFree(name);
676         inf->usri1_comment[0] = 0;
677         inf->usri1_flags =
678             UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD;
679         inf->usri1_full_name[0] = 0;
680         inf->usri1_user_id = 0;
681         inf->usri1_next_index = 0;
682
683         inf++;
684         ACCESS_CopyDisplayUser(admin, &str, inf);
685         NetApiBufferFree(admin);
686
687         inf++;
688         ACCESS_CopyDisplayUser(guest, &str, inf);
689         NetApiBufferFree(guest);
690         break;
691     }
692
693     case 2:
694     case 3:
695     {
696         FIXME("Level %d is not implemented\n", Level);
697         break;
698     }
699
700     default:
701         TRACE("Invalid level %d is specified\n", Level);
702         return ERROR_INVALID_LEVEL;
703     }
704     return NERR_Success;
705 }
706
707 /************************************************************
708  *                NetGetDCName  (NETAPI32.@)
709  *
710  *  Return the name of the primary domain controller (PDC)
711  */
712
713 NET_API_STATUS WINAPI
714 NetGetDCName(LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr)
715 {
716   FIXME("(%s, %s, %p) stub!\n", debugstr_w(servername),
717                  debugstr_w(domainname), bufptr);
718   return NERR_DCNotFound; /* say we can't find a domain controller */  
719 }
720
721 /************************************************************
722  *                NetGroupEnum  (NETAPI32.@)
723  *
724  */
725 NET_API_STATUS WINAPI
726 NetGroupEnum(LPCWSTR servername, DWORD level, LPBYTE *bufptr, DWORD prefmaxlen,
727              LPDWORD entriesread, LPDWORD totalentries, LPDWORD resume_handle)
728 {
729     FIXME("(%s, %d, %p, %d, %p, %p, %p) stub!\n", debugstr_w(servername),
730           level, bufptr, prefmaxlen, entriesread, totalentries, resume_handle);
731     return ERROR_ACCESS_DENIED;
732 }
733
734 /************************************************************
735  *                NetGroupGetInfo  (NETAPI32.@)
736  *
737  */
738 NET_API_STATUS WINAPI NetGroupGetInfo(LPCWSTR servername, LPCWSTR groupname, DWORD level, LPBYTE *bufptr)
739 {
740     FIXME("(%s, %s, %d, %p) stub!\n", debugstr_w(servername), debugstr_w(groupname), level, bufptr);
741     return ERROR_ACCESS_DENIED;
742 }
743
744 /******************************************************************************
745  * NetUserModalsGet  (NETAPI32.@)
746  *
747  * Retrieves global information for all users and global groups in the security
748  * database.
749  *
750  * PARAMS
751  *  szServer   [I] Specifies the DNS or the NetBIOS name of the remote server
752  *                 on which the function is to execute.
753  *  level      [I] Information level of the data.
754  *     0   Return global passwords parameters. bufptr points to a
755  *         USER_MODALS_INFO_0 struct.
756  *     1   Return logon server and domain controller information. bufptr
757  *         points to a USER_MODALS_INFO_1 struct.
758  *     2   Return domain name and identifier. bufptr points to a 
759  *         USER_MODALS_INFO_2 struct.
760  *     3   Return lockout information. bufptr points to a USER_MODALS_INFO_3
761  *         struct.
762  *  pbuffer    [I] Buffer that receives the data.
763  *
764  * RETURNS
765  *  Success: NERR_Success.
766  *  Failure: 
767  *     ERROR_ACCESS_DENIED - the user does not have access to the info.
768  *     NERR_InvalidComputer - computer name is invalid.
769  */
770 NET_API_STATUS WINAPI NetUserModalsGet(
771     LPCWSTR szServer, DWORD level, LPBYTE *pbuffer)
772 {
773     TRACE("(%s %d %p)\n", debugstr_w(szServer), level, pbuffer);
774     
775     switch (level)
776     {
777         case 0:
778             /* return global passwords parameters */
779             FIXME("level 0 not implemented!\n");
780             *pbuffer = NULL;
781             return NERR_InternalError;
782         case 1:
783             /* return logon server and domain controller info */
784             FIXME("level 1 not implemented!\n");
785             *pbuffer = NULL;
786             return NERR_InternalError;
787         case 2:
788         {
789             /* return domain name and identifier */
790             PUSER_MODALS_INFO_2 umi;
791             LSA_HANDLE policyHandle;
792             LSA_OBJECT_ATTRIBUTES objectAttributes;
793             PPOLICY_ACCOUNT_DOMAIN_INFO domainInfo;
794             NTSTATUS ntStatus;
795             PSID domainIdentifier = NULL;
796             int domainNameLen;
797
798             ZeroMemory(&objectAttributes, sizeof(objectAttributes));
799             objectAttributes.Length = sizeof(objectAttributes);
800
801             ntStatus = LsaOpenPolicy(NULL, &objectAttributes,
802                                      POLICY_VIEW_LOCAL_INFORMATION,
803                                      &policyHandle);
804             if (ntStatus != STATUS_SUCCESS)
805             {
806                 WARN("LsaOpenPolicy failed with NT status %x\n",
807                      LsaNtStatusToWinError(ntStatus));
808                 return ntStatus;
809             }
810
811             ntStatus = LsaQueryInformationPolicy(policyHandle,
812                                                  PolicyAccountDomainInformation,
813                                                  (PVOID *)&domainInfo);
814             if (ntStatus != STATUS_SUCCESS)
815             {
816                 WARN("LsaQueryInformationPolicy failed with NT status %x\n",
817                      LsaNtStatusToWinError(ntStatus));
818                 LsaClose(policyHandle);
819                 return ntStatus;
820             }
821
822             domainIdentifier = domainInfo->DomainSid;
823             domainNameLen = lstrlenW(domainInfo->DomainName.Buffer) + 1;
824             LsaClose(policyHandle);
825
826             ntStatus = NetApiBufferAllocate(sizeof(USER_MODALS_INFO_2) +
827                                             GetLengthSid(domainIdentifier) +
828                                             domainNameLen * sizeof(WCHAR),
829                                             (LPVOID *)pbuffer);
830
831             if (ntStatus != NERR_Success)
832             {
833                 WARN("NetApiBufferAllocate() failed\n");
834                 LsaFreeMemory(domainInfo);
835                 return ntStatus;
836             }
837
838             umi = (USER_MODALS_INFO_2 *) *pbuffer;
839             umi->usrmod2_domain_id = *pbuffer + sizeof(USER_MODALS_INFO_2);
840             umi->usrmod2_domain_name = (LPWSTR)(*pbuffer +
841                 sizeof(USER_MODALS_INFO_2) + GetLengthSid(domainIdentifier));
842
843             lstrcpynW(umi->usrmod2_domain_name,
844                       domainInfo->DomainName.Buffer,
845                       domainNameLen);
846             CopySid(GetLengthSid(domainIdentifier), umi->usrmod2_domain_id,
847                     domainIdentifier);
848
849             LsaFreeMemory(domainInfo);
850
851             break;
852         } 
853         case 3:
854             /* return lockout information */
855             FIXME("level 3 not implemented!\n");
856             *pbuffer = NULL;
857             return NERR_InternalError;
858         default:
859             TRACE("Invalid level %d is specified\n", level);
860             *pbuffer = NULL;
861             return ERROR_INVALID_LEVEL;
862     }
863
864     return NERR_Success;
865 }
866
867 /******************************************************************************
868  *                NetUserChangePassword  (NETAPI32.@)
869  * PARAMS
870  *  domainname  [I] Optional. Domain on which the user resides or the logon
871  *                  domain of the current user if NULL.
872  *  username    [I] Optional. Username to change the password for or the name
873  *                  of the current user if NULL.
874  *  oldpassword [I] The user's current password.
875  *  newpassword [I] The password that the user will be changed to using.
876  *
877  * RETURNS
878  *  Success: NERR_Success.
879  *  Failure: NERR_* failure code or win error code.
880  *
881  */
882 NET_API_STATUS WINAPI NetUserChangePassword(LPCWSTR domainname, LPCWSTR username,
883     LPCWSTR oldpassword, LPCWSTR newpassword)
884 {
885     struct sam_user *user;
886
887     TRACE("(%s, %s, ..., ...)\n", debugstr_w(domainname), debugstr_w(username));
888
889     if(domainname)
890         FIXME("Ignoring domainname %s.\n", debugstr_w(domainname));
891
892     if((user = NETAPI_FindUser(username)) == NULL)
893         return NERR_UserNotFound;
894
895     if(lstrcmpW(user->user_password, oldpassword) != 0)
896         return ERROR_INVALID_PASSWORD;
897
898     if(lstrlenW(newpassword) > PWLEN)
899         return ERROR_PASSWORD_RESTRICTION;
900
901     lstrcpyW(user->user_password, newpassword);
902
903     return NERR_Success;
904 }
905
906 NET_API_STATUS WINAPI NetUseAdd(LMSTR servername, DWORD level, LPBYTE bufptr, LPDWORD parm_err)
907 {
908     FIXME("%s %d %p %p stub\n", debugstr_w(servername), level, bufptr, parm_err);
909     return NERR_Success;
910 }