Unix Application Migration Guide (Patterns & Practices)
The UNIX and Windows security models are quite different. Win32 uses the underlying Windows security model. This results in some key differences between the way Win32 security works and the way UNIX security works. (Some of these differences have already been covered in Comparison of Windows and UNIX Architectures in Chapter 2, Windows and UNIX Compared.) This section covers the differences in the security model and how you should modify your code to operate in Win32.
The key areas that are addressed here are:
-
A comparison of the UNIX and Win32 user and group APIs
-
Adding a new group
-
Adding a user to a group
-
Listing groups
-
Adding a user account
-
Changing a user s password
-
Removing a user account
-
Getting user information about all users
-
Getting information about a specific user
-
Retrieving the current user s user name
-
Security functions
UNIX and Win32 User and Group Functions
This section describes the different user and group functions for UNIX and Win32.
UNIX User and Group Functions
Table 9.9 shows user and group management functions that control user and group accounts in a security database. To link some of the code examples that use these functions, you must add -lcrypt to the gcc option list.
Function | Description |
---|---|
Group database functions | |
endgrent | Close database |
fgetgrent, fgetgrent_r | Get next Group database entry from FILE Stream |
getgrent, getgrent_r | Get next Group database entry |
getgrgid, getgrgid_r | Get Group database entry with Group ID |
getgrnam, getgrnam_r | Get Group database entry with Name |
setgrent | Rewind database |
Supplementary group access list functions | |
getgroups | Get |
initgroups | Initialize |
setgroups | Set |
User shadow database functions | |
endspent | Close database |
fgetspent, fgetspent_r | Get next User Shadow database entry from FILE Stream |
getspent, getspent_r | Get next User Shadow database entry |
getspnam, getspnam_r | Get User Shadow database entry with Name |
setspent | Rewind database |
User database functions | |
endpwent | Close database |
fgetpwent, fgetpwent_r | Get next User database entry from FILE Stream |
getpw | User database get function to get passwd entry from UID |
getpwent, getpwent_r | Get next User database entry |
getpwnam, getpwnam_r | Get User database entry with Name |
getpwuid, getpwuid_r | Get User database entry with User ID |
setpwent | Rewind database |
User database Lock/UnLock functions | |
lckpwdf | Lock function |
ulckpwdf | UnLock function |
User database Write functions | |
putgrent | Write group file entry |
putpwent | Write password file entry |
putspent | Write shadow password file entry |
Win32 User Functions
Table 9.10 shows Win32 user management functions that control a user s account in a security database. To link these functions in the example code later in this section, you must add Netapi32.lib to the Visual Studio project link-library list.
Function | Description |
---|---|
NetUserAdd | Adds a user account and assigns a password and privilege level. |
NetUserChangePassword | Changes a user s password for a specified network server or domain. |
NetUserDel | Deletes a user account from the server. |
NetUserEnum | Lists all user accounts on a server. |
NetUserGetGroups | Returns a list of global group names to which a user belongs. |
NetUserGetInfo | Returns information about a particular user account on a server. |
NetUserGetLocalGroups | Returns a list of local group names to which a user belongs. |
NetUserSetGroups | Sets global group memberships for a specified user account. |
NetUserSetInfo | Sets the password and other elements of a user account. |
User account information is available at the following levels:
-
USER_INFO_0
-
USER_INFO_1
-
USER_INFO_2
-
USER_INFO_3
-
USER_INFO_4
-
USER_INFO_10
-
USER_INFO_11
-
USER_INFO_20
-
USER_INFO_21
-
USER_INFO_22
-
USER_INFO_23
In addition, the following information levels are valid when you call the NetUserSetInfo function:
-
USER_INFO_1003
-
USER_INFO_1005
-
USER_INFO_1006
-
USER_INFO_1007
-
USER_INFO_1008
-
USER_INFO_1009
-
USER_INFO_1010
-
USER_INFO_1011
-
USER_INFO_1012
-
USER_INFO_1014
-
USER_INFO_1017
-
USER_INFO_1020
-
USER_INFO_1024
-
USER_INFO_1051
-
USER_INFO_1052
-
USER_INFO_1053
Win32 Group Functions
The network management provides functions for both local groups and global groups.
Local Group
A local group can contain user accounts or global group accounts from one or more domains. (Global groups can contain users from only one domain.) A local group shares common privileges and rights only within its own domain.
The network management local group functions control members of local groups in a way that the functions can only be called locally on the system on which the local group is defined. On a Windows NT, Windows 2000, or Windows XP workstation,or on a server that is not a domain controller, you can use only a local group defined on that system. A local group defined on the primary domain controller is replicated to all other domain controllers in the domain. Therefore, a local group is availableon all domain controllers within the domain in which it was created.
The local group functions create or delete local groups, and review or adjust the memberships of local groups. These functions are shown in Table 9.11 on the next page.
Function | Description |
---|---|
NetLocalGroupAdd | Creates a local group. |
NetLocalGroupAddMembers | Adds one or more users or global groups to an existing local group. |
NetLocalGroupDel | Deletes a local group, removing all existing members from the group. |
NetLocalGroupDelMembers | Removes one or more members from an existing local group |
NetLocalGroupEnum | Returns information about each local group account on a server. |
NetLocalGroupGetInfo | Returns information about a particular local group account on a server. |
NetLocalGroupGetMembers | Lists all members of a specified local group. |
NetLocalGroupSetInfo | Sets general information about a local group. |
NetLocalGroupSetMembers | Assigns members to a local group. |
Global Group
A global group contains user accounts from one domain that are grouped together under one group account name. A global group can contain only members (users) from the domain where the global group is created; it cannot contain local groupsor other global groups. A global group is available within its own domain and within any trusting domain.
The network management group functions control global groups. Table 9.12 shows these group functions.
Function | Description |
---|---|
NetGroupAdd | Creates a global group. |
NetGroupAddUser | Adds one user to an existing global group. |
NetGroupDel | Removes a global group whether or not the group has any members. |
NetGroupDelUser | Removes one user name from a global group. |
NetGroupEnum | Lists all global groups on a server. |
NetGroupGetInfo | Returns information about a particular global group. |
NetGroupGetUsers | Lists all members of a particular global group. |
NetGroupSetInfo | Sets general information about a global group. |
NetGroupSetUsers | Assigns members to a new global group. Replaces the members of an existing group. |
Global group account information is available at the following levels:
-
GROUP_INFO_0
-
GROUP_INFO_1
-
GROUP_INFO_2
-
GROUP_INFO_3
-
GROUP_INFO_1002
-
GROUP_INFO_1005
Adding a New Group
The following examples illustrate migrating code from UNIX to Windows to adda new group.
UNIX Example: Adding a New Group
This example uses the getgrnam and getgruid functions to verify that a group account and gid does not already exists before adding a new group account with the putgrent function.
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <grp.h> int main(int argc, char *argv[]) { struct group a_group; struct group *ptr_group; char gr_name[20]; char gr_passwd[] = ""; char *gr_mem[] = {0}; int i, gid; a_group.gr_name = gr_name; a_group.gr_passwd = gr_passwd; a_group.gr_mem = gr_mem; if (argc != 3) { printf("Usage: %s GroupName GID\n", argv[0]); exit(1); } // Call the getpwnam function to check if user exists. if(ptr_group = getgrnam (argv[1])) { printf("Group already exists\n"); exit(1); } // Call the getpwuid function to check if User ID exists if(ptr_group = getgrgid (gid=(gid_t) atoi(argv[2]))) { printf("Group ID already exists\n"); exit(1); } ptr_group = &a_group; strcpy (ptr_group->gr_name, argv[1]); printf ("Group: %s\n", ptr_group->gr_name); ptr_group->gr_gid = gid; printf ("gid: %d\n", ptr_group->gr_gid); // Call the putgrent function putgrent(ptr_group, fopen ("/etc/group", "a+")); exit(0); }
Win32 Example: Adding a New Group
This example uses the NetLocalGroupAdd() function to create a local group in the security database.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { LOCALGROUP_INFO_1 lgi; DWORD dwLevel = 1; DWORD dwError = 0; NET_API_STATUS nStatus; if (argc != 3) { fwprintf(stderr, L"Usage: %s \\ServerName GroupName\n", argv[0]); exit(1); } // Set up the LOCALGROUP_INFO_1 structure. lgi.lgrpi1_name = argv[2]; lgi.lgrpi1_comment = NULL; nStatus = NetLocalGroupAdd(argv[1], dwLevel, (LPBYTE)&lgi, &dwError); // // If the call succeeds, inform the user. // if (nStatus == NERR_Success) fwprintf(stderr, L"Local Group %s has been successfully added on %s\n", argv[2], argv[1]); // // Otherwise, print the system error. // else fprintf(stderr, "A system error has occurred: %d\n", nStatus); return 0; }
Adding a User to a Group
The following examples illustrate migrating code from UNIX to Windows to adda user to a group.
UNIX Example: Adding a User to a Group
This example uses the getgrnam and getpwnam functions to verify that the specified group account exists, and that the user account to be added exists, before creatinga new /etc/group file (named: /etc/groupx) with the added member to the specified group using a combination of the fgetgrent and fprintf functions. This program assumes that the super user will be using this program, and will manually copy /etc/groupx to /etc/group after verifying proper update to the group entry.
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <grp.h> #include <pwd.h> int main(int argc, char *argv[]) { struct group a_group; struct group *ptr_group; struct passwd *ptr_pswd; int i; FILE *stream_in, *stream_out; if (argc != 3) { printf("Usage: %s GroupName NewMember\n", argv[0]); exit(1); } // Call the getgrnam function to check if group exists. if(!(ptr_group = getgrnam (argv[1]))) { printf("group does not exist\n"); exit(1); } // Call the getpwnam function to check if group exists. if(!(ptr_pswd = getpwnam (argv[2]))) { printf("member to be added does not exist\n"); exit(1); } ptr_group = &a_group; // Scan /etc/group file, create /etc/groupx, with updated group entry stream_in = fopen ("/etc/group", "r"); stream_out = fopen ("/etc/groupx", "w"); while(ptr_group = fgetgrent(stream_in)) { i=0; fprintf(stream_out, "%s:%s:%d:", ptr_group->gr_name,\ ptr_group->gr_passwd, ptr_group->gr_gid); while(ptr_group->gr_mem[i] != 0) { fprintf(stream_out, "%s,", ptr_group->gr_mem[i]); i++; } if(strcmp(ptr_group->gr_name, argv[1]) == 0) fprintf(stream_out, "%s\n", argv[2]); else fprintf(stream_out, "\n"); printf("Next Group: %s\n", ptr_group->gr_name); } fclose (stream_in); fclose (stream_out); exit(0); }
Win32 Example: Adding a User to a Group
This example uses the NetLocalGroupAddMembers () function to add membersto a local group in the security database.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { LOCALGROUP_MEMBERS_INFO_3 lgmi; DWORD dwLevel = 3; DWORD dwCount = 1; NET_API_STATUS nStatus; if (argc != 4) { fwprintf(stderr, L"Usage: %s \\ServerName GroupName UserName\n", argv[0]); exit(1); } // Set up the LOCALGROUP_MEMBERS_INFO_3 structure. lgmi.lgrmi3_domainandname = argv[3]; nStatus = NetLocalGroupAddMembers(argv[1], argv[2], dwLevel, (LPBYTE)&lgmi, dwCount); // // If the call succeeds, inform the user. // if (nStatus == NERR_Success) fwprintf(stderr, L"User %s has been successfully added to Local Group %s on %s\n", argv[3], argv[2], argv[1]); // // Otherwise, print the system error. // else fprintf(stderr, "A system error has occurred: %d\n", nStatus); return 0; }
Listing Groups
The following examples illustrate migrating code to list groups from UNIXto Windows.
UNIX Example: Listing Groups
This example uses the getgroups function to retrieve a list of global groups to which the calling user belongs.
#include <sys/types.h> #include <unistd.h> #include <grp.h> int main() { struct group *ptr_group; gid_t grouplist[100]; int num; num = getgroups (99, grouplist); printf("group\tID:\n-------------\n"); if (num > 0) while(num) { ptr_group = getgrgid(grouplist[num]); printf("%s\t%d\n", ptr_group->gr_name, grouplist[num]); } else printf("num=%d\n", num); exit(0); }
Win32 Example: Listing Groups
This example uses the NetUserGetGroups() function to retrieve a list of global groups to which a specified user belongs. You use the NetUserGetLocalGroups() function to get a list of local groups to which a user belongs.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <assert.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { LPGROUP_USERS_INFO_0 pBuf = NULL; DWORD dwLevel = 0; DWORD dwPrefMaxLen = -1; DWORD dwEntriesRead = 0; DWORD dwTotalEntries = 0; NET_API_STATUS nStatus; if (argc != 3) { fwprintf(stderr, L"Usage: %s \\ServerName UserName\n", argv[0]); exit(1); } // // Call the NetUserGetGroups function, specifying level 0. // nStatus = NetUserGetGroups(argv[1], argv[2], dwLevel, (LPBYTE*)&pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries); // // If the call succeeds, // if (nStatus == NERR_Success) { LPGROUP_USERS_INFO_0 pTmpBuf; DWORD i; DWORD dwTotalCount = 0; if ((pTmpBuf = pBuf) != NULL) { fprintf(stderr, "\nGlobal group(s):\n"); // // Loop through the entries; // print the name of the global groups // to which the user belongs. // for (i = 0; i < dwEntriesRead; i++) { assert(pTmpBuf != NULL); if (pTmpBuf == NULL) { fprintf(stderr, "An access violation has occurred\n"); break; } wprintf(L"\t %s\n", pTmpBuf->grui0_name); pTmpBuf++; dwTotalCount++; } } // // If all available entries were // not enumerated, print the number actually // enumerated and the total number available. // if (dwEntriesRead < dwTotalEntries) fprintf(stderr, "\nTotal entries: %d", dwTotalEntries); // // Otherwise, just print the total. // printf("\nEntries enumerated: %d\n", dwTotalCount); } else fprintf(stderr, "A system error has occurred: %d\n", nStatus); // // Free the allocated buffer. // if (pBuf != NULL) NetApiBufferFree(pBuf); return 0; }
Adding a User Account
The following examples illustrate migrating code to add user accounts from UNIX to Windows.
UNIX Example: Adding a User Account
This example uses the getpwnam and getpwuid functions to verify that a user account and uid does not already exist before adding a new user account with the putpwent function. This program assumes that a user home directory with the same name has already been created under /home, and does not verify that this directory exists.
#define _XOPEN_SOURCE #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> int main(int argc, char *argv[]) { struct passwd pswd; struct passwd *ptr_pswd; char pw_name[20]; char pw_passwd[30]; char pw_dir[26]; char pw_shell[15]; int uid; pswd.pw_name = pw_name; pswd.pw_passwd = pw_passwd; pswd.pw_dir = pw_dir; pswd.pw_shell = pw_shell; if (argc != 5) { printf("Usage: %s UserName Password UID GID\n", argv[0]); exit(1); } // Call the getpwnam function to check if user exists. if(ptr_pswd = getpwnam (argv[1])) { printf("user already exists\n"); exit(1); } // Call the getpwuid function to check if User ID exists if(ptr_pswd = getpwuid (uid=(uid_t) atoi(argv[3]))) { printf("user ID already exists\n"); exit(1); } ptr_pswd = &pswd; strcpy (ptr_pswd->pw_name, argv[1]); printf ("Name: %s\n", ptr_pswd->pw_name); strcpy (ptr_pswd->pw_passwd, crypt(argv[2],"az")); ptr_pswd->pw_uid = uid; printf ("uid: %d\n", ptr_pswd->pw_uid); ptr_pswd->pw_gid = (gid_t) atoi(argv[4]); printf ("gid: %d\n", ptr_pswd->pw_gid); strcpy (ptr_pswd->pw_dir, "/home/"); strcat (ptr_pswd->pw_dir, argv[1]); printf ("Home Dir: %s\n", ptr_pswd->pw_dir); strcpy (ptr_pswd->pw_shell, "/bin/bash"); printf ("Shell: %s\n", ptr_pswd->pw_shell); // Call the putpwent function putpwent(ptr_pswd, fopen ("/etc/passwd", "a+")); exit(0); }
Win32 Example: Adding a User Account
This example uses NetUserAdd() function to add a user account and assignsa password and privilege level.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { USER_INFO_1 ui; DWORD dwLevel = 1; DWORD dwError = 0; NET_API_STATUS nStatus; if (argc != 3) { fwprintf(stderr, L"Usage: %s \\ServerName UserName\n", argv[0]); exit(1); } // // Set up the USER_INFO_1 structure. // USER_PRIV_USER: name identifies a user, // rather than an administrator or a guest. // UF_SCRIPT: required for LAN Manager 2.0 and // Windows NT and later. // ui.usri1_name = argv[2]; ui.usri1_password = argv[2]; ui.usri1_priv = USER_PRIV_USER; ui.usri1_home_dir = NULL; ui.usri1_comment = NULL; ui.usri1_flags = UF_SCRIPT; ui.usri1_script_path = NULL; // // Call the NetUserAdd function, specifying level 1. // nStatus = NetUserAdd(argv[1], dwLevel, (LPBYTE)&ui, &dwError); // // If the call succeeds, inform the user. // if (nStatus == NERR_Success) fwprintf(stderr, L"User %s has been successfully added on %s\n", argv[2], argv[1]); // // Otherwise, print the system error. // else fprintf(stderr, "A system error has occurred: %d\n", nStatus); return 0; }
Changing a User s Password
The following examples illustrate migrating code to change a user s password from UNIX to Windows.
UNIX Example: Changing a User s Password
This example uses the getpwnam function to verify that the user account exists before creating a new /etc/passwd file (named /etc/passwdx) with the entered password using a combination of the fgetpwent and fprintf functions. This program assumes that the super user will be using this program, and will manually copy /etc/passwdx to /etc/passwd after verifying proper update to the user entries.
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> int main(int argc, char *argv[]) { struct passwd pswd; struct passwd *ptr_pswd; FILE *stream_in, *stream_out; if (argc != 3) { printf("Usage: %s UserName Password\n", argv[0]); exit(1); } // Call the getpwnam function to check if user exists. if(!(ptr_pswd = getpwnam (argv[1]))) { printf("user does not exist\n"); exit(1); } ptr_pswd = &pswd; // Scan /etc/passwd file, create /etc/passwdx, with updated user entry stream_in = fopen ("/etc/passwd", "r"); stream_out = fopen ("/etc/passwdx", "w"); while(ptr_pswd = fgetpwent(stream_in)) { if(strcmp(ptr_pswd->pw_name, argv[1]) == 0) strcpy (ptr_pswd->pw_passwd, crypt(argv[2],"az")); fprintf(stream_out, "%s:%s:%d:%d:%s:%s:%s\n", ptr_pswd->pw_name,\ ptr_pswd->pw_passwd, ptr_pswd->pw_uid, ptr_pswd->pw_gid,\ ptr_pswd->pw_gecos, ptr_pswd->pw_dir, ptr_pswd->pw_shell); printf("Next Name: %s\n", ptr_pswd->pw_name); } fclose (stream_in); fclose (stream_out); exit(0); }
Win32 Example: Changing a User s Password
This example uses the NetUserChangePassword() function to change a user s password for a specified network server or domain.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { DWORD dwError = 0; NET_API_STATUS nStatus; // // All parameters are required. // if (argc != 5) { fwprintf(stderr, L"Usage: %s \\ServerName UserName OldPassword NewPassword\n", argv[0]); exit(1); } // // Call the NetUserChangePassword function. // nStatus = NetUserChangePassword(argv[1], argv[2], argv[3], argv[4]); // // If the call succeeds, inform the user. // if (nStatus == NERR_Success) fwprintf(stderr, L"User password has been changed successfully\n"); // // Otherwise, print the system error. // else fprintf(stderr, "A system error has occurred: %d\n", nStatus); return 0; }
Removing a User Account
The following examples illustrate migrating code to remove user accounts from UNIX to Windows.
UNIX Example: Removing a User Account
This example uses the getpwnam function to verify that the user account exists before creating a new /etc/passwd file (named /etc/passwdx). It does so with the specified user account removed, using a combination of the fgetpwent and fprintf functions. This program assumes that the super user will be using this program, and will manually copy /etc/passwdx to /etc/passwd after verifying proper update to the user entries.
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> int main(int argc, char *argv[]) { struct passwd pswd; struct passwd *ptr_pswd; FILE *stream_in, *stream_out; if (argc != 2) { printf("Usage: %s UserName\n", argv[0]); exit(1); } // Call the getpwnam function to check if user exists. if(!(ptr_pswd = getpwnam (argv[1]))) { printf("user does not exist\n"); exit(1); } ptr_pswd = &pswd; // Scan /etc/passwd file, create /etc/passwdx, with updated user entry stream_in = fopen ("/etc/passwd", "r"); stream_out = fopen ("/etc/passwdx", "w"); while(ptr_pswd = fgetpwent(stream_in)) { if(strcmp(ptr_pswd->pw_name, argv[1]) != 0) fprintf(stream_out, "%s:%s:%d:%d:%s:%s:%s\n", ptr_pswd->pw_name,\ ptr_pswd->pw_passwd, ptr_pswd->pw_uid, ptr_pswd->pw_gid,\ ptr_pswd->pw_gecos, ptr_pswd->pw_dir, ptr_pswd->pw_shell); printf("Next Name: %s\n", ptr_pswd->pw_name); } fclose (stream_in); fclose (stream_out); exit(0); }
Win32 Example: Removing a User Account
This example uses the NetUserDel() function to delete a user account from a server.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { DWORD dwError = 0; NET_API_STATUS nStatus; // // All parameters are required. // if (argc != 3) { fwprintf(stderr, L"Usage: %s \\ServerName UserName\n", argv[0]); exit(1); } // // Call the NetUserDel function to delete the share. // nStatus = NetUserDel(argv[1], argv[2]); // // Display the result of the call. // if (nStatus == NERR_Success) fwprintf(stderr, L"User %s has been successfully deleted on %s\n", argv[2], argv[1]); else fprintf(stderr, "A system error has occurred: %d\n", nStatus); return 0; }
Getting User Information About All Users
The following examples illustrate migrating code to get user information from UNIX to Windows.
UNIX Example: Getting User Information About All Users
This example uses the getpwent function to provide information about all available user accounts.
#include <stdio.h> #include <unistd.h> #include <pwd.h> int main() { struct passwd pswd; struct passwd *ptr_pswd; // Scan /etc/passwd file while(ptr_pswd = getpwent()) printf("Account Name: %s\n", ptr_pswd->pw_name); exit(0); }
Win32 Example: Getting User Information About All Users
This example uses the NetUserEnum() function to provide information about all the users on a server.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <assert.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { LPUSER_INFO_0 pBuf = NULL; LPUSER_INFO_0 pTmpBuf; DWORD dwLevel = 0; DWORD dwPrefMaxLen = -1; DWORD dwEntriesRead = 0; DWORD dwTotalEntries = 0; DWORD dwResumeHandle = 0; DWORD i; DWORD dwTotalCount = 0; NET_API_STATUS nStatus; LPTSTR pszServerName = NULL; if (argc > 2) { fwprintf(stderr, L"Usage: %s [\\ServerName]\n", argv[0]); exit(1); } // The server is not the default local computer. // if (argc == 2) pszServerName = argv[1]; wprintf(L"\nUser account on %s: \n", pszServerName); // // Call the NetUserEnum function, specifying level 0; // enumerate global user account types only. // do // begin do { nStatus = NetUserEnum(pszServerName, dwLevel, FILTER_NORMAL_ACCOUNT, // global users (LPBYTE*)&pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries, &dwResumeHandle); // // If the call succeeds, // if ((nStatus == NERR_Success) (nStatus == ERROR_MORE_DATA)) { if ((pTmpBuf = pBuf) != NULL) { // // Loop through the entries. // for (i = 0; (i < dwEntriesRead); i++) { assert(pTmpBuf != NULL); if (pTmpBuf == NULL) { fprintf(stderr, "An access violation has occurred\n"); break; } // // Print the name of the user account. // wprintf(L"\t %s\n", pTmpBuf->usri0_name); pTmpBuf++; dwTotalCount++; } } } // // Otherwise, print the system error. // else fprintf(stderr, "A system error has occurred: %d\n", nStatus); // // Free the allocated buffer. // if (pBuf != NULL) { NetApiBufferFree(pBuf); pBuf = NULL; } } // Continue to call NetUserEnum while // there are more entries. // while (nStatus == ERROR_MORE_DATA); // end do // // Check again for allocated memory. // if (pBuf != NULL) NetApiBufferFree(pBuf); // // Print the final count of users enumerated. // fprintf(stderr, "\nTotal of %d entries enumerated\n", dwTotalCount); return 0; }
Getting Information About a Specific User
The following examples illustrate migrating code from UNIX to Windows to get information about a specific user account.
UNIX Example: Getting Information About a Specific User
This example uses the getpwnam function to obtain the information and output the account information of a specified user.
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <pwd.h> int main(int argc, char *argv[]) { struct passwd *ptr_pswd; int uid; if (argc != 2) { printf("Usage: %s UserName\n", argv[0]); exit(1); } // Call the getpwnam function to check if user exists. if(!(ptr_pswd = getpwnam (argv[1]))) { printf("user does not exist\n"); exit(1); } printf ("Name: %s\n", ptr_pswd->pw_name); printf ("uid: %d\n", ptr_pswd->pw_uid); printf ("gid: %d\n", ptr_pswd->pw_gid); printf ("Home Dir: %s\n", ptr_pswd->pw_dir); printf ("Shell: %s\n", ptr_pswd->pw_shell); exit(0); }
Win32 Example: Getting Information About a Specific User
This example uses NetUserGetInfo() function to retrieve information about a particular user account on a server. This function returns specific information in USER INFO structure based on different levels. For more details, see the Platform SDK documentation.
#ifndef UNICODE #define UNICODE #endif #include <stdio.h> #include <windows.h> #include <lm.h> int wmain(int argc, wchar_t *argv[]) { DWORD dwLevel = 10; LPUSER_INFO_10 pBuf = NULL; NET_API_STATUS nStatus; if (argc != 3) { fwprintf(stderr, L"Usage: %s \\ServerName UserName\n", argv[0]); exit(1); } // // Call the NetUserGetInfo function; specify level 10. // nStatus = NetUserGetInfo(argv[1], argv[2], dwLevel, (LPBYTE *)&pBuf); // // If the call succeeds, print the user information. // if (nStatus == NERR_Success) { if (pBuf != NULL) { wprintf(L"\n\tAccount: %s\n", pBuf->usri10_name); wprintf(L"\tComment: %s\n", pBuf->usri10_comment); wprintf(L"\tUser comment: %s\n", pBuf->usri10_usr_comment); wprintf(L"\tFull name: %s\n", pBuf->usri10_full_name); } } // Otherwise, print the system error. // else fprintf(stderr, "A system error has occurred: %d\n", nStatus); // // Free the allocated memory. // if (pBuf != NULL) NetApiBufferFree(pBuf); return 0; }
Retrieving the Current User s User Name
The following examples illustrate migrating code from UNIX to Windows to retrieve the current user s user name.
UNIX Example: Retrieving the Current User s User Name
This example uses the getlogin() function to retrieve the user name of the user currently logged onto the system.
#include <stdio.h> #include <unistd.h> main() { // Get and display the user name. printf("User name: %s\n", getlogin()); }
Win32 Example: Retrieving the Current User s User Name
This example uses GetUserName() function to retrieve the user name of the current thread. This is the name of the user currently logged on to the system.The GetUserNameEx() function can be used to retrieve the user name in a specified format.
#include <windows.h> #include <stdio.h> #include <lmcons.h> void main() { LPTSTR lpszSystemInfo; // pointer to system information string DWORD cchBuff = 256; // size of user name TCHAR tchBuffer[UNLEN + 1]; // buffer for expanded string lpszSystemInfo = tchBuffer; // Get and display the user name. GetUserName(lpszSystemInfo, &cchBuff); printf("User name: %s\n", lpszSystemInfo); }
Security Functions
Windows security enables you to control the ability of a process to access securable objects or to perform various system administration tasks . Application developers use access control to control access to securable objects such as files, registry keys, and directory service objects.
Table 9.13 shows the two basic components of the Windows access-control model.
Access-control component | Description |
---|---|
Access tokens | Access tokens contain information about a logged-on user. |
Security descriptors | Security descriptors contain the security information that protects a securable object. |
When a user logs on to a Windows system, the system authenticates the user s account name and password. If the logon is successful, the system creates an access token. Every process executed on behalf of this user will have a copy of this access token. The access token contains security identifiers (SIDs) that identify the user s account and any group accounts to which the user belongs. The token also contains a list of the privileges held by the user or the user s groups. The system uses this token to identify the associated user when a process tries to access a securable object or perform a system administration task that requires privileges.
An access-control list (ACL) is a list of access-control entries (ACEs). Each ACE inan ACL identifies a trustee and specifies the access rights allowed, denied , or audited for that trustee. The security descriptor for a securable object can contain two ACLs: a DACL and a SACL.
A DACL (discretionary access-control list) identifies the trustees that are allowed or denied access to a securable object. When a process tries to access a securable object, the system checks the ACEs in the object s DACL to determine whether to grant access to it. If the object does not have a DACL, the system grants full access to everyone.If the object s DACL has no ACEs, the system denies all attempts to access the object because the DACL does not allow any access rights. The system checks the ACEs in sequence until it finds one or more ACEs that allow all the requested access rights, or until any of the requested access rights are denied.
A system access-control list (SACL) enables administrators to log attempts to access a secured object. Each ACE specifies the types of access attempts by a specified trustee that cause the system to generate a record in the security event log. An ACE in a SACL can generate audit records when an access attempt fails, when it succeeds, or both. In future releases, a SACL can also raise an alarm when an unauthorized user attempts to gain access to an object.
UNIX Example: Using Security Functions
This example and the following Win32 example illustrate migrating code from UNIX to Windows that uses basic security functions.
#include <stdio.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <err.h> #include <pwd.h> int main() { struct passwd pswd; struct stat stat_info; uid_t uid; int rtn; struct passwd * ptr_pswd = &pswd; // Get the owner UID of the file. // and Check error code. if (stat ("myfile.txt", &stat_info) == -1) err(1, NULL); uid = stat_info.st_uid; // Lookup Account UID to get the owners name. // and Check error code. if(ptr_pswd = getpwuid (uid)) // Print the account name. printf("owner: %s\n", ptr_pswd->pw_name); else err(1, NULL); }
Win32 Example: Using Security Functions
This example uses the GetSecurityInfo() and LookupAccountSid() functionsto find and print the name of the owner of a file. The file exists in the current working directory on the local server.
#include <stdio.h> #include <windows.h> #include <tchar.h> #include "accctrl.h" #include "aclapi.h" int main(int argc, char **argv) { DWORD dwRtnCode = 0; PSID pSidOwner; BOOL bRtnBool = TRUE; LPTSTR AcctName, DomainName; DWORD dwAcctName = 1, dwDomainName = 1; SID_NAME_USE eUse = SidTypeUnknown; HANDLE hFile; PSECURITY_DESCRIPTOR pSD; LPVOID lpMsgBuf; // Get the handle of the file object. hFile = CreateFile("myfile.txt", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); // Check GetLastError for CreateFile error code. if (hFile == INVALID_HANDLE_VALUE) { FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER FORMAT_MESSAGE_FROM_SYSTEM FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language (LPTSTR) &lpMsgBuf, 0, NULL); _tprintf(TEXT("Error message:%s\n"), lpMsgBuf); LocalFree(lpMsgBuf); return -1; } // Allocate memory for the SID structure. pSidOwner = (PSID)GlobalAlloc(GMEM_FIXED, sizeof(PSID)); // Allocate memory for the security descriptor structure. pSD = (PSECURITY_DESCRIPTOR)GlobalAlloc(GMEM_FIXED, sizeof(PSECURITY_DESCRIPTOR)); // Get the owner SID of the file. dwRtnCode = GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSD); // Check GetLastError for GetSecurityInfo error condition. if (dwRtnCode != ERROR_SUCCESS) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); _tprintf(TEXT("GetSecurityInfo error = %d\n"), dwErrorCode); return -1; } // First call to LookupAccountSid to get the buffer sizes. DomainName = NULL; AcctName = NULL; bRtnBool = LookupAccountSid(NULL, // local computer pSidOwner, AcctName, (LPDWORD)&dwAcctName, DomainName, (LPDWORD)&dwDomainName, &eUse); // Reallocate memory for the buffers. AcctName = (char *)GlobalAlloc(GMEM_FIXED, dwAcctName); // Check GetLastError for GlobalAlloc error condition. if (AcctName == NULL) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); _tprintf(TEXT("GlobalAlloc error = %d\n"), dwErrorCode); return -1; } DomainName = (char *)GlobalAlloc(GMEM_FIXED, dwDomainName); // Check GetLastError for GlobalAlloc error condition. if (DomainName == NULL) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); _tprintf(TEXT("GlobalAlloc error = %d\n"), dwErrorCode); return -1; } // Second call to LookupAccountSid to get the account name. bRtnBool = LookupAccountSid(NULL, // name of local or remote computer pSidOwner, // security identifier AcctName, // account name buffer (LPDWORD)&dwAcctName, // size of account name buffer DomainName, // domain name (LPDWORD)&dwDomainName, // size of domain name buffer &eUse); // SID type // Check GetLastError for LookupAccountSid error condition. if (bRtnBool == FALSE) { DWORD dwErrorCode = 0; dwErrorCode = GetLastError(); if (dwErrorCode == ERROR_NONE_MAPPED) _tprintf(TEXT("Account owner not found for specified SID.\n")); else _tprintf(TEXT("Error in LookupAccountSid.\n")); return -1; } else if (bRtnBool == TRUE) // Print the account name. _tprintf(TEXT("Account owner = %s\n"), AcctName); return 0; }