get the default email from the user on a Linux box - c

Is there any way to programmatically get the current user's email address?
I know the email is usually user#hostname but is there any I can get the email?
I know how to get the username and the hostname so I can build it myself, but I want to be sure that I get the email address even when the email is not user#hostname.
Code in C is appreciated.
Thanks

There is no such standard mapping of user account to email address - at least not for ordinary /etc/passwd derived accounts. Consider that a user might not even have an email address.

Nobody's mentioned the GECOS fields in the /etc/passwd file.
You'll notice that the fifth field in your entry in /etc/passwd is either blank, or a comma-separated list the first element of which is your full name. Originally in Bell Labs (before the days of email) the GECOS fields were:
User's full name (or application name, if the account is for a
program)
Building and room number or contact person
Office telephone
number
Any other contact information (pager number, fax, etc.)
Some Linux distributions store the user's default email address in the 4th GECOS field, and if your system doesn't do this by default, you can set it up yourself. Ordinary users without superuser privilege can edit their GECOS fields using the command line command chfn. To access this field, you can then do
grep ${USER}: /etc/passwd | awk -F\: '{print $5}' | awk -F\, '{print $4}'
or whatever floats your boat in your language of choice (No, I am NOT going to write C. This is the twenty-first century!).

There is no standard mapping of user accounts to RFC822 (i.e. user#domain) email addresses. Generally, a default setup of typical mail transfer agents will accept local mail to addresses without a domain and deliver it to the user account of the same name. But even that can't be relied on, as you may not even have an MTA.

The UNIX way of doing this is to send email through the local mail-transfer-agent - simply invoking /usr/bin/mail is enough. The system administrator is responsible for configuring the local MTA to make sure email works properly.
If you want to send email to the local user, just send it to their username - if they read their email somewhere other than locally, the MTA should be configured to forward it to them.
If you just want to use the right "from" email address when sending email on behalf of a local user, so they get replies in the right place - again, just use their username. The MTA should be configured to do the right translation.
This way of doing things is good, because it means that this configuration only has to be done in one place (the MTA), rather than having to manually configure every single application on the box that sends or recieves email.

Just to complement Simon's answer and given I don't have enough reputation to make a comment on it, GECOS stands for General Comprehensive Operating System aka General Electric Comprehensive Operating Supervisor and the most portable way I found to get the user GECOS field (As it might not be defined in your /etc/passwd file directly depending on your system's configuration) is the following:
getent passwd <USERNAME> | awk -F ':' '{print $5}'

It depends how the user is stored. In a simple passwd file there's no email address, only a username. But you can have additional information with other authentication method like LDAP or SQL.

Prompt the user for their email. If you have no guarantee that the email is user#hostname, then how else do you expect to determine what their email is other than asking them?

You can't get the actual email address in any standard way. I would try to send the mail to just username. Chanses that it will end up on the correct domain are actually not that bad ...

Check in the terminal you're using, that is :
root#peter-laptop#
for root users it is shown before the # sign, that is
root#peter-laptop or peter#peter-laptop# for user peter

Try to get to /var/mail/ and there you should have a file for each user that has (not all users have to have it) an email address. And you can indeed read the mail from those files.
Then you can redirect the mail to anywhere else with the sendmail tool.

Related

Are there known issues with the C function crypt()?

I use crypt() to encrypt passwords for a project of mine. When the password is chosen by the user, it's encrypted like this:
password = crypt(<password chosen>, <user's account name>)
The problem is when the user logs in using their password. If what they type doesn't match their password, it should enter this if check:
if (strcmp(crypt(<what user types in as password>, <user's account name>), <user's encrypted password>)) {
//...
}
It doesn't in one certain scenario. Let's say their password is 'asdf'. If they enter 'asdf' with any random trailing characters, such as 'asdffffff' or 'asdf339sfd', it still accepts the password. It seems to ignore everything after 'asdf'.
Is this a known issue with crypt? Is there another way to encrypt passwords?
The second argument to crypt is not supposed to be the user's account name. It's supposed to be a setting string. Setting strings look like this:
$2b$07$fQuDK3TaQP4sw6IX6iVcTw
The $2b$07$ part tells crypt which password-hashing algorithm to use, and the string of random characters that follows is the salt. The salt must be different for each user, but it is not supposed to have any correlation with the user's account name. It doesn't technically have to be random, but it's critical that it be different for each user, and it needs to change every time the user changes their password, so best practice is to use a long string drawn from a cryptographic PRNG.
When you authenticate a user who has logged in before, you use the stored hashed password as the setting string:
char *new_hash = crypt("password typed in", "stored hash");
if (new_hash && !strcmp(new_hash, "stored hash")) {
// user has successfully logged in
}
This works because the stored hashed password always begins with the setting string that was used to create it in the first place, and crypt is coded to look only at the setting-string part.
(Also note the null check; some implementations of crypt can fail and report failure by returning a null pointer.)
When you create a new account or change a password, you have to generate a new setting string. If you have the function crypt_gensalt, use that:
char *new_setting = crypt_gensalt("$2b$", 0, 0, 0);
if (new_setting) {
char *new_hash = crypt("user's new password", new_setting);
// ...
} else {
// halt and catch fire
}
If you don't have crypt_gensalt you have to implement it yourself, unfortunately. (To make matters worse, some Unixes have the crypt_gensalt whose documentation I linked to above, and others have a different version, with the same name, doing the same job, but taking different arguments. Time to dust off your Autoconf skillz!)
Now you know all of that, I can explain why
password = crypt("password chosen", "user's account name");
appeared to work but truncated the password. Your user account names probably begin with at least two alphanumeric characters, right? Like, "Ma[ya]", or "zw[ol]"? Unfortunately, any two alphanumeric characters make a valid setting string ... that selects one of the oldest and least secure password-hashing algorithms known to science, descrypt. (It was pretty good when it was invented ... in the mid-1970s. Nowadays, it can be cracked by brute force no matter what the password is.) One of the many problems with this algorithm is that it truncates all passwords to eight characters. asdf and asdfhjkl hash to different things, but asdfhjkl and asdfhjkl1234 hash to the same thing.
The cure for this is to use crypt_gensalt or equivalent to select a modern algorithm. All the modern algorithms accept arbitrarily long passphrases.

What is the proper way to get some specific user attributes from Active Directory via LDAP C API?

I am trying to get some user attributes from Active Directry using Windows LDAP API. I am using:
Active Directory Version: The one that comes with Windows Server 2012
LDAP version: 3
Wldap32.lib version: The one that comes with Windows 10 x64
Eg:
PCHAR myAttributes[4];
myAttributes[0] = "DistinguishedName";
myAttributes[1] = "DisplayName";
myAttributes[2] = "PasswordExpired";
myAttributes[3] = "mail";
ldap_search_s(
myLdapConnection, // Ldap connection
myDomain, // DN to start search
LDAP_SCOPE_SUBTREE, // Scope
myFilter, // Filter
myAttributes, // Retrieve list of attributes
0, // Get both attributes and values
&mySearchResult // [out] Search results
);
It returns DistinguishedName, DisplayName and mail attributes, but does not reuturn the PasswordExpired attribute.
I queried with some other attributes and it looks like it does not return attributes with boolean values as well as the EmailAddress attribute.
Why does it not return PasswordExpired attribute?
What about EmailAddress ?
Is there a difference between EmailAddress and mail ?
There is no PasswordExpired LDAP attribute in Active Directory. To build your query look at this URL for attribute names in standard Active Directory schema https://msdn.microsoft.com/en-us/library/ms675090(v=vs.85).aspx. To check if password for given account is expired you'll need to check userAccountControl attribute, which is actually value storing flags of different states of user account https://msdn.microsoft.com/en-us/library/ms680832(v=vs.85).aspx. There is IADsUser interface that will translate this all for you in case you don't have to stick just to LDAP https://msdn.microsoft.com/en-us/library/aa746343(v=vs.85).aspx
You will find current primary email address in mail attribute. There is no EmailAddress LDAP attribute, unless you meant E-mail-Addresses which is CN for the same schema attribute as mail, so no difference there.
See above. In general if you do not have compelling reason to stick just to C/LDAP I'd recommend you to use .Net Framework instead. Otherwise you have a lot of work ahead of you - not just interpreting bit flags like in case of password expiration but possibly also with different authentication methods, different structures capturing time and date, accounting for timezones, UTF, chasing referrals and other stuff you might need depending on complexity of what you want to achieve. You will be productive much faster in .Net Framework. See DirectoryServices https://msdn.microsoft.com/en-us/library/mt481534(v=vs.110).aspx namespace or Security namespace https://msdn.microsoft.com/en-us/library/mt481561(v=vs.110).aspx for details.

Active Directory: Map a domain user with a username from ForeignSecurityPrinciple group and check if user is part of another specific group

I have this problem: we try to authenticate a user against an Active Directory that uses ForeignSecurityPrinciple to map users from different domains.
We have several groups that are stored inside a single domain and users that are coming from different domains.
The problem we have here is that we cannot get the real user name from ForeignSecurityPrinciple.
We tried to use the guidelines from here:
http://www.brandonclaps.com/?p=57
but with no luck.
When we tried to edit the entry for group that had 3 members, we found this next to the member field:
CN = S-1-5-21-other numbers, CN=ForeignSecurityPrinciple; DC=DomainName, DC=local
Do you have other solutions for retrieving the username against Active Directory using ForeginSecurityPrinciple ?
Is there any recommendation for using that?
Thanks a lot in advance

How do I build this LDAP connection string?

I'm trying to retrieve user information in Active Directory, but am finding some things to be poorly documented. For example, the LDAP connection strings I have seen contain some keywords that I don't know the meaning of. Here's a sample:
LDAP://ofmdcoly302.ofm.wa.lcl/ou=employees,dc=ofm,dc=wa
What are the keywords "ou" and "dc" supposed to signify? In our case "ou=employees" seems to identify a particular AD node. However when I try to do a .FindAll() on the above directory entry, I get "A referral was returned from the server". I guess that means it couldn't find what I was looking for, but it might be available somewhere else? In one place I read "A referral is AD's way of saying, 'this object probably exists in another domain'".
What are "ou" and "dc" supposed to mean? And if I had this A/D structure, how would I code the LDAP connetion string to retrieve information in the "AR" node:
ou and dc are not keywords, they are attributes, "organizational unit" and "domain component", respectively. Attributes are defined in the directory server schema. Attributes are gathered together into entries, of which the distinguished name is the primary key.
Taken in this context, ou and dc are "distinguished name components", together comprising a "distinguished name", which is the aforementioned primary key of an LDAP entry. ou=employees,dc=ofm,dc=wa is a distinguished name composed of the various relative distinguished name components.
This entry has two mistakes: it's not using root (dc=lcl), you also skipped one ou, should be ou=employees,ou=Users - OFMGOV,dc=ofm,dc=wa,dc=lcl
TBH if your really want to understand this notation I would suggest using ADSI Edit instead of ADUC that you probably use ATM - it will show you path the same way, so it will be easier to translate. It will also prevent you from shooting yourself in a foot with special containers like 'CN=Users' and 'CN=Computers':
For the OU you have highlighted it would most likely be:
ou=AR,ou=Citrix,ou=Users - OFMGOV,dc=ofm,dc=wa,dc=lcl

Get user security attributes on AIX 5

Is there a way to get user security attibutes (like password expiration warning time, maximum age for password and so on) by AIX 5 API?
Look at the getuserpw() API for password information, and getuserattr() for the more generic user information.
Note that you will have to be a member of the appropriate group(s) in order to access the files in /etc/security.

Resources