perl LDAP entry not recognised - perl

We are writing a Perl code (to be run from Unix) which will reset the password of a Windows AD User. (We are not using powershell as we have been asked not to use Windows scripts).
With the following Perl code, we are able to connect to the AD User directory and query the correct user.
#!/usr/bin/perl -w
#########################
#This script resets the password in active user directory
#########################
use strict;
use warnings;
use DBI;
use Net::LDAP;
use Net::LDAPS;
use Authen::SASL qw(Perl);
use Net::LDAP::Control::Paged;
use Time::Local;
my $CERTDIR = "<cert path>";
my $AD_PASS = "$CERTDIR/.VDIAD_pass";
my $sAN = "vahmed";
### Generate Random Password ###
my $randompass = askPasswd();
my $uninewpass;
my $mail;
my $fullname;
my $name;
my $distName;
my $finalresult;
my #AD_passwords = get_domain_pass();
my $result = reset_AD_Password();
#Reset AD user password
sub reset_AD_Password {
my $ad = Net::LDAP->new($AD_passwords[0]);
my $msg = $ad->bind(dn => "cn=$AD_passwords[2],$AD_passwords[1]",
password => $AD_passwords[3],
version => 3);
if ($msg->code)
{
print "Error :" . $msg->error() . "\n";
exit 2;
}
my $acc_name = 'sAMAccountName';
my $acc_fullname = 'displayName';
my $acc_base = 'manager';
my $acc_distName = 'distinguishedName';
my $acc_mail = 'mail';
my $act = $ad->search(
base => "$AD_passwords[1]",
filter => "(&(objectCategory=person)(sAMAccountName=$sAN))",
attrs => [$acc_name, $acc_fullname, $acc_distName, $acc_mail]);
die 1 if ($act->count() !=1 );
my $samdn = $act->entry(0)->dn;
$fullname = $samdn->get_value($acc_fullname);
$mail = $samdn->get_value($acc_mail);
}
}
However we get an error on the line:
$fullname = $samdn->get_value($acc_fullname);
$mail = $samdn->get_value($acc_mail);
The error states "Can't locate object method "get_value" via package (distinguished Name) (perhaps you forgot to load (distinguished Name))"
However the code works correctly when we replace $samdn with the following code:
foreach my $entry ($act->entries){
$name = $entry->get_value($acc_name);
$fullname = $entry->get_value($acc_fullname);
$distName = $entry->get_value($acc_distName);
$mail = $entry->get_value($acc_mail);
}
It would appear that the code is unable to identify $samdn as a Net::LDAP::Entry record.
We have tried typecasting $samdn but got the same error.
Could someone help in resolving this issue as we would not prefer to use the for loop just in case more that one record is returned by the search? Thanks in advance.

You are not assigning a Net::LDAP::Entry to $samdn. You are assigning the dn of the first entry.
# VVVV
my $samdn = $act->entry(0)->dn;
Get rid of that ->dn and it should work, if $act->entry(0) returns a Net::LDAP::Entry.

Related

using Net::LDAPs with Net::LDAP::Control::Paged

I'm trying to use Net::LDAPs with Net::LDAP::CONTROL::PAGED to return many records via a privlidged bind, but so far I have failed, miserably. I've used this Net::LDAPs extensively in the past, but I've never been able to find any documentation suggesting that it is compatible with Net::LDAP:Control::Paged. Everything I find is related to Net::LDAP.
The error message I get is: Undefined subroutine &main::process_entry called at /usr/local/share/perl/5.20.2/Net/LDAP/Search.pm line 55, line 755
Here is my code:
sub Ldap636{
my ($filter) = $_[0];
my $USERNAME = 'username';
my $PASSWORD = 'password';
my $LDAP_SERVER = 'directory.domain.edu';
my $LDAP_SSL_PORT = '636';
my $LDAP_BASE = 'ou=people,dc=domain,dc=edu';
my $userDN = "uid=$USERNAME,ou=identities,ou=special,dc=domain,dc=edu";
my $ldap = Net::LDAPS->new($LDAP_SERVER, port => $LDAP_SSL_PORT) or die "Could not create LDAP object because:\n$!";
my $ldapMsg = $ldap->bind($userDN, password => $PASSWORD);
die $ldapMsg->error if $ldapMsg->is_error;
my $page = Net::LDAP::Control::Paged->new( size => 100 );
#args = (base => "$LDAP_BASE",
callback => \&process_entry,
filter => $filter,
control => [ $page ],
);
my $cookie;
while (1) {
my $result = $ldap->search(#args);
"LDAP error: server says ",$result->error,"\n" if $result->code;
foreach my $entry ($result->entries ) {
my $cn = $entry->get_value('cn');
my $desc = $entry->get_value('description');
print "$cn - $desc\n";
}
# Get cookie from paged control
my($resp) = $result->control( LDAP_CONTROL_PAGED ) or last;
$cookie = $resp->cookie or last;
$page->cookie($cookie);
}
$ldap->unbind;
}
The error message I get is: Undefined subroutine &main::process_entry
called at /usr/local/share/perl/5.20.2/Net/LDAP/Search.pm line 55,
line 755
You have written process_entry as a callback but you didn't write that subroutine. That's why you are getting the above error.

Fetch all user information with Net::LDAP

Currently have an small perl script what for the given username fetch his email address from the ActiveDirectory using Net::LDAP.
The search part is the following:
my $user = "myuser";
my $mesg = $ldap->search(
base => "dc=some,dc=example,dc=com",
filter => '(&(sAMAccountName=' . $user . ')(mail=*))', #?!?
);
for my $entry ($mesg->entries) {
my $val = $entry->get_value('mail');
say "==$val==";
}
Working ok.
How i should modify the above statement to fetch all available information for the given user myuser? I'm looking to get an perl-ish data structure, such something like next:
my $alldata = search(... all info for the given $user ... );
say Dumper $alldata; #hashref with all stored informations for the $user
It is probably dead simple - but i'm an total AD & LDAP-dumb person...
Edit: When I dump out the $msg->entries (what is an LADP::Entry object) got something, but i'm not sure than it contains everything or only the part of the stored data...
I've done something similar, and I use this to query LDAP:
my $ldapResponse = $ldap->search(base => $base, filter => $filter, attrs => $attrs);
And then this to parse it:
if ($ldapResponse && $ldapResponse->count()) {
$ldapResponse->code && die $ldapResponse->error;
my %domainNames = %{$ldapResponse->as_struct};
foreach my $domainName (keys %domainNames) {
my %ldapResponse;
my %dnHash = %{$domainNames{$domainName}};
foreach my $attr (sort(keys %dnHash)) {
# Note that the value for each key of %dnHash is an array,
# so join it together into a string.
my $value = join(" ", #{$dnHash{$attr}});
$ldapResponse{$attr} = $value;
}
// Dump/use %ldapResponse
}
}
I've never tried to use the ldap->entries in your code, but the above works for me!
I explicitly specify a(long) list of attributes ($attr), but perhaps that's optional as your example shows, and you can get ALL LDAP fields by just skipping that arg to search().

I can't get proper Name&Domain properties using Win32_Process::GetOwner call under Perl

I want to enumerate all running processes' name&domain propeties using Win32_Process::GetOwner method under Perl, my code was listed below:
use strict;
use warnings;
use Win32::OLE('in');
use Win32::OLE::Variant;
my $retval;
my $machine = ".";
my $outParam;
my $CLASS = "Winmgmts:{impersonationlevel=impersonate}!\\\\$machine\\root\\cimv2";
my $WMI = Win32::OLE->GetObject($CLASS) or die("GetObject:" . Win32::OLE->LastError());
my $process = $WMI->InstancesOf("Win32_Process");
my $vtName = Win32::OLE::Variant->new(VT_CY, VT_EMPTY);
my $vtDomain = Win32::OLE::Variant->new(VT_CY, VT_EMPTY);
foreach my $obj(in $process)
{
$retval = $obj->GetOwner($vtName, $vtDomain);
if($retval == 0){
print $vtName->Value() . "\t" . $vtDomain->Value() . "\n";
}
}
The script can run properly, but I got the "zero" echo string everytime:
Who can tell me what caused this problem and how to fix it?
Thank you!
Seems Karthik already has the code you're looking for:
my $objWMIService = Win32::OLE->GetObject("winmgmts:\\\\localhost\\root\\CIMV2");
my $sqry = "SELECT * FROM Win32_Process where Name = \"$processName\" ";`
Also seems someone else has the same issue where Perl issues a status code of 0 instead of the actual data:
http://code.activestate.com/lists/perl-win32-users/16230/

Perl - How to get the email address from the FROM part of header?

I am trying to set up this script for my local bands newsletter.
Currently, someone sends an email with a request to be added, we manually add it to newsletter mailer I set up.
(Which works great thanks to help I found here!)
The intent now is to have my script below log into the email account I set up for the list on our server, grab the info to add the email automatically.
I know there are a bunch of apps that do this but, I want to learn myself.
I already have the "add to list" working when there is an email address returned from the header(from) below BUT, sometimes the header(from) is a name and not the email address (eg "persons name" is returned from persons name<email#address> but, not the <email#address>.)
Now, I am not set in stone on the below method but, it works famously... to a point.
I read all the docs on these modules and there was nothing I could find to get the darn email in there all the time.
Can someone help me here? Verbose examples are greatly appreciated since I am struggling learning Perl.
#!/usr/bin/perl -w
##########
use CGI;
use Net::IMAP::Simple;
use Email::Simple;
use IO::Socket::SSL; #optional i think if no ssl is needed
use strict;
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
######################################################
# fill in your details here
my $username = '#########';
my $password = '#############';
my $mailhost = '##############';
#######################################################
print CGI::header();
# Connect
my $imap = Net::IMAP::Simple->new($mailhost, port=> 143, use_ssl => 0, ) || die "Unable to connect to IMAP: $Net::IMAP::Simple::errstr\n";
# Log in
if ( !$imap->login( $username, $password ) ) {
print STDERR "Login failed: " . $imap->errstr . "\n";
exit(64);
}
# Look in the INBOX
my $nm = $imap->select('INBOX');
# How many messages are there?
my ($unseen, $recent, $num_messages) = $imap->status();
print "unseen: $unseen, <br />recent: $recent, <br />total: $num_messages<br />\n\n";
## Iterate through unseen messages
for ( my $i = 1 ; $i <= $nm ; $i++ ) {
if ( $imap->seen($i) ) {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
printf( "[%03d] %s\n\t%s\n", $i, $es->header('From'), $es->header('Subject'));
print "<br />";
next;
}## in the long version these are pushed into different arrays for experimenting purposes
else {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
printf( "[%03d] %s\n\t%s\n", $i, $es->header('From'), $es->header('Subject'));
print "<br />";
}
}
# Disconnect
$imap->quit;
exit;
use Email::Address;
my #addresses = Email::Address->parse('persons name <email#address>');
print $addresses[0]->address;
The parse method returns an array, so the above way works for me.
I'm making this a separate answer because even though this information is hidden in the comments of the accepted answer, it took me all day to figure that out.
First you need to get the From header using something like Email::Simple. THEN you need to extract the address portion with Email::Address.
use Email::Simple;
use Email::Address;
my $email = Email::Simple->new($input);
my $from = $email->header('From');
my #addrs = Email::Address->parse($from);
my $from_address = $addrs[0]->address; # finally, the naked From address.
Those 4 steps in that order.
The final step is made confusing by the fact that Email::Address uses some voodoo where if you print the parts that Email::Address->parse returns, they will look like simple strings, but they are actually objects. For example if you print the result of Email::Address->parse like so,
my #addrs = Email::Address->parse($from);
foreach my $addr (#addrs) { say $addr; }
You will get the complete address as output:
"Some Name" <address#example.com>
This was highly confusing when working on this. Granted, I caused the confusion by printing the results in the first place, but I do that out of habit when debugging.

How can I get the date of an email using Perl's Mail::MboxParser::Mail?

This is a simple question. I have a little program here that reads
a list of emails in a specific inbox of a user account specified by the program.
I can access an account using its username, password and host. The only problem is I don't know how to get the date on each of these mails.
Here's some part of my code:
my $pop = new Mail::POP3Client(
USER => $user, #some user,password & host assigned
PASSWORD => $pass,
HOST => $host );
for( $i = 1; $i <= $pop->Count(); $i++ ) {
#header = $pop->Head($i);
#body = $pop->Body($i);
$mail = new Mail::MboxParser::Mail(\#header, \#body);
$user_email = $mail->from()->{email
print "Email:".$user_email; #this prints out right
foreach( $pop->Head( $i ) ) {
/^(Date):\s+/i && print $_, "\n";
$date = $_;
}
}
Now what i need is to get the only one date for each email,
but that loop gives me all.. but when remove the loop, it
returns an error. I'm using Perl.
Kindly help me? :)
According to MboxParser::Email doc, you should be able to do:
$date = $mail->header->{'date'}; #Keys are all lowercase
If you have more than one date returned, $date will be an array ref and you can access the first occurence of the Date with:
$date->[0];
So you shouldn't need to loop through the header and use a regular expression.