Wednesday, December 7, 2011

How to check password expiration date programmatically (Windows, Linux, Solaris, AIX)

Many operating systems allow you to set maximum password age. After it is reached, user gets the message like “Your password has expired and must be changed”. For instance, in case of Windows 7 you can set maximum password age in the following way:

1. Run “secpol.msc”
2. In the left pane, expand “Account Policies”, and click on “Password Policy”. Edit the values in the right pane.
3. Make sure that user account has password expiration enabled: run lusrmgr.msc, find the required user account, click proiperties, ensure that “Password never expires” is uchecked.

(see http://www.sevenforums.com/tutorials/7539-local-users-groups-manager-open.html, http://www.sevenforums.com/tutorials/73210-password-expiration-enable-disable.html for details).

The post describes the API that allows you to find password expiration date for a specified user account.

1. Windows: use NetUserGetInfo function. For a given domain controller name, user name and information level it returns user information. In our case we need information level 2 (and probably higher). In this case information is returned in USER_INFO_2 which has a field called usri2_password_age (the number of seconds that have elapsed since the password was last changed).

Now check the NetUserModalsGet fuction. For a given domain controller name it returns global information about users (see struct USER_MODALS_INFO_0, which has a usrmod0_max_passwd_age member). Now just subtract usri2_password_age from usrmod0_max_passwd_age and divide by 60*60*24 (the number of seconds in a day) to get count of days left.

To get the domain contoller you may use NetGetAnyDCName fuction (for instance, call NetGetAnyDCName(null, L“MYDOMAIN”, &controller) to get the domain controller of MYDOMAIN).

All of the functions mentioned above allocate memory, so do not forget to free it using the NetApiBufferFree function.

Samples can be found in MSDN.

2. Linux and Solaris provide a set of shadow fuctions (#include <shadow.h>).
We are interested in getspnam function.
For a given user name It returns a pointer to struct spwd. Interesting members are sp_lstchg (days since Jan 1, 1970 password was last changed) and sp_max (days after which password must be changed). So (spwd.sp_lstchg + spwd.sp_max) is a date when user’s password must be changed.
More information is available in manual.

3. AIX. I haven’t found a reliable way to find password expiration date. A possible solution is to call passwdexpired function. For a given user name it returns a character string like “Your password will expire: Wed Nov 2 10:30:35 EDT 2011”. Now it is possible to parse the string. However I am not sure in the format of message and hence I dislike this solution.
If you know any way to get the password expiration date, please let me know!

Friday, December 2, 2011

How to get process full name programmatically (Solaris)

In the previous post I described how to get the command line and the full name of process executable file on Linux. However, the described solution does not work on Solaris.

Solaris has the /proc pseudo-filesystem directory as well, but it’s structure is different.

On Solaris 10 there is a link /proc/PID/path/a.out which points to the executable file. So it’s enough to call readlink function.
Prior to Solaris 10, things are more complex. I haven’t found a reliable way to get a full name of the executable file. But the following notes may be useful.

There is a binary /proc/<PID>/psinfo file, which stores data as in struct psinfo (#include <procfs.h>). This struct has pr_fname and pr_psargs fields. But it seems that pr_fname usually holds the file name of executable only, without the absolute path. The second problem is that both pr_fname and pr_psargs can be truncated.

There is a function called getexecname(), which returns file name of the executable. But it has the same problem: it may hold the file name part only, without the absolute path. Manual recommends getcwd function which returns the current working directory. However, current working directory may differ from the starting directory so this solution is not reliable.

So the following program illustrates all of these approaches:

#include <stdio.h>
#include <unistd.h>
#include <procfs.h>
#include <stdlib.h>

int main() {
ssize_t linknamelen;
FILE *f;
char linkname[256], filename[256];
pid_t pid = getpid();

/* try to readlink the file /proc/PID/path/a.out */
sprintf(linkname, "/proc/%d/path/a.out", pid);
linknamelen = readlink(linkname, filename, sizeof(filename) / sizeof(*filename) - 1);
if (linknamelen > 0) {
 filename[linknamelen] = 0;
 printf("Full name is %s\n", filename);
} else {
 getcwd(filename, sizeof(filename) / sizeof(*filename) - 1);
 printf("Current directory is %s. Name is %s\n", filename, getexecname());
}

/* read the command line from /proc/PID/psinfo */
sprintf(filename, "/proc/%d/psinfo", pid);

f = fopen(filename, "r");
if (f) {
psinfo_t info;
if (fread(&info, sizeof(info), 1, f) > 0) {
    printf("fname=%s, args=%s\n", info.pr_fname, info.pr_psargs);
}
fclose(f);
}

return 0;
}