summaryrefslogtreecommitdiffstats
path: root/scripts/ldap
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/ldap')
-rwxr-xr-xscripts/ldap/radiusd2ldif.pl307
-rw-r--r--scripts/ldap/schema_to_samba.py132
2 files changed, 439 insertions, 0 deletions
diff --git a/scripts/ldap/radiusd2ldif.pl b/scripts/ldap/radiusd2ldif.pl
new file mode 100755
index 0000000..4dbd04f
--- /dev/null
+++ b/scripts/ldap/radiusd2ldif.pl
@@ -0,0 +1,307 @@
+#!/usr/bin/perl
+
+# radius2ldif.pl
+#
+# To test this program, do the following
+#Take a radius users' file, for example with:
+#
+#myuser Password = "apassword"
+# User-Service = Framed-User,
+# Framed-Protocol = PPP,
+# Framed-Address = 255.255.255.255,
+# Framed-Netmask = 255.255.255.255,
+# Ascend-Metric = 2,
+# Framed-Routing = None,
+# Framed-Compression = 0,
+# Ascend-Idle-Limit = 0,
+# Ascend-Maximum-Time = 36000
+#
+#and do:
+#
+#cat users | ./radius2ldif
+#
+#Output is:
+#dn: cn=myuser, ou=Hardware, ou=EDUCAMADRID, ou=People, o=icm.es
+#objectclass: top
+#objectclass: person
+#objectclass: radiusprofile
+#cn: myuser
+#sn: myuser
+#userpassword: apassword
+#radiusServiceType: Framed-User
+#radiusFramedProtocol: PPP
+#radiusFramedIPAddress: 255.255.255.255
+#radiusFramedIPNetmask: 255.255.255.255
+#radiusFramedRouting: None
+#radiusFramedCompression: 0
+#
+#dn: ou=RadiusUser, ou=Groups, o=icm.es
+#description: RadiusUser
+#objectclass: top
+#objectclass: groupOfUniqueNames
+#cn: RadiusUser
+#uniquemember: dn: cn=myuser, ou=Hardware, ou=EDUCAMADRID, ou=People, o=icm.es
+#
+# (c) 2000 Javier Fern'andez-Sanguino Pen~a <jfs@computer.org>
+# -------------------------------------------------------------------------
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+# -----------------------------------------------------------------------
+
+
+# TODO:
+# currently does not encrypt passwords (takes them from outside file)
+
+# Command line options
+# -d : debugging output
+# -p : give only password
+# -m : set entry to modify ldap attributes
+# -f : read encrypted passwords from file
+use Getopt::Std;
+getopts('dpmf:');
+$debug = $opt_d;
+
+%passwords;
+# This might or might not be necessary depending if your LDAP server
+# when importing from ldif introduces crypted passwords in the LDAP db
+# (not necessary for Netscape's Directory Server)
+read_passwds ($opt_f) if $opt_f;
+
+# USER CONFIGURATION
+# ------------------
+$usermatch = ".*"; # only add users matching this
+# WARNING: in order to add *all* users set this to ".*" NOT ""
+
+# LDAP configuration
+$domain = "o=icm.es";
+$basedn = ", ou=Hardware, ou=EDUCAMADRID, ou=People, $domain";
+$predn = "dn: cn=";
+$uniquemembers = 1;
+$groupname = "RadiusUser"; # group to add in the LDAP, if null will not add
+$group = "\n\ndn: ou=$groupname, ou=Groups, $domain";
+# Only useful for adding the group (not yet implemented)
+$addgroup = $group."\ndescription: $groupname\nobjectclass: top";
+if ( $uniquemembers ) {
+$addgroup = $addgroup."\nobjectclass: groupOfUniqueNames";
+} else {
+$addgroup = $addgroup."\nobjectclass: groupOfNames";
+}
+$addgroup = $addgroup."\ncn: $groupname";
+# The following group must be created first
+# (ldif entry), the script will *not* create it
+#cn=$group,ou=Groups,o=icm.es
+#description=whatever
+#objectclass=top
+#objectclass=groupOfUniqueNames
+# (or objectclass=groupOfNames)
+#cn=$group
+# Required: person (for userpasswords) and radiusprofile (<draft-aboba-radius-02.txt> 5 February 1998)
+@objectClass = ( "top", "person" , "radiusprofile" );
+
+
+# Mapping of entries (use lower case so no check needs to be make)
+# From freeradius: rlm_ldap.c
+# { "radiusServiceType", "Service-Type" },
+# { "radiusFramedProtocol", "Framed-Protocol" },
+# { "radiusFramedIPAddress", "Framed-IP-Address" },
+# { "radiusFramedIPNetmask", "Framed-IP-Netmask" },
+# { "radiusFramedRoute", "Framed-Route" },
+# { "radiusFramedRouting", "Framed-Routing" },
+# { "radiusFilterId", "Filter-Id" },
+# { "radiusFramedMTU", "Framed-MTU" },
+# { "radiusFramedCompression", "Framed-Compression" },
+# { "radiusLoginIPHost", "Login-IP-Host" },
+# { "radiusLoginService", "Login-Service" },
+# { "radiusLoginTCPPort", "Login-TCP-Port" },
+# { "radiusCallbackNumber", "Callback-Number" },
+# { "radiusCallbackId", "Callback-Id" },
+# { "radiusFramedRoute", "Framed-Route" },
+# { "radiusFramedIPXNetwork", "Framed-IPX-Network" },
+# { "radiusClass", "Class" },
+# { "radiusSessionTimeout", "Session-Timeout" },
+# { "radiusIdleTimeout", "Idle-Timeout" },
+# { "radiusTerminationAction", "Termination-Action" },
+# { "radiusCalledStationId", "Called-Station-Id" },
+# { "radiusCallingStationId", "Calling-Station-Id" },
+# { "radiusLoginLATService", "Login-LAT-Service" },
+# { "radiusLoginLATNode", "Login-LAT-Node" },
+# { "radiusLoginLATGroup", "Login-LAT-Group" },
+# { "radiusFramedAppleTalkLink", "Framed-AppleTalk-Link" },
+# { "radiusFramedAppleTalkNetwork", "Framed-AppleTalk-Network" },
+# { "radiusFramedAppleTalkZone", "Framed-AppleTalk-Zone" },
+# { "radiusPortLimit", "Port-Limit" },
+# { "radiusLoginLATPort", "Login-LAT-Port" },
+# You can change to the mappings below like this
+# cat radius2ldif.pl | grep ^# | \
+# perl -ne 'if ( /\{ \"(.*?)\", \"(.*?)\" \}/ ) \
+# { $attr=lc $2; print "\$mapping{\"$attr\"} = \"$1\";\n" ; } '
+
+
+# Warning: sometimes password must be encrypted before sent to the LDAP
+# Which Perl libraries are available? Only way I find is through
+# Netscape's NDS getpwenc.
+# However NDS does the cyphering even if sending plain passwords
+# (do all LDAP's do this?)
+# TODO: test with OpenLDAP
+$mapping{'password'} = "userpassword";
+$mapping{'service-type'} = "radiusServiceType";
+$mapping{'framed-protocol'} = "radiusFramedProtocol";
+$mapping{'framed-ip-address'} = "radiusFramedIPAddress";
+$mapping{'framed-ip-netmask'} = "radiusFramedIPNetmask";
+$mapping{'framed-route'} = "radiusFramedRoute";
+$mapping{'framed-routing'} = "radiusFramedRouting";
+$mapping{'filter-id'} = "radiusFilterId";
+$mapping{'framed-mtu'} = "radiusFramedMTU";
+$mapping{'framed-compression'} = "radiusFramedCompression";
+$mapping{'login-ip-host'} = "radiusLoginIPHost";
+$mapping{'login-service'} = "radiusLoginService";
+$mapping{'login-tcp-port'} = "radiusLoginTCPPort";
+$mapping{'callback-number'} = "radiusCallbackNumber";
+$mapping{'callback-id'} = "radiusCallbackId";
+$mapping{'framed-ipx-network'} = "radiusFramedIPXNetwork";
+$mapping{'class'} = "radiusClass";
+$mapping{'session-timeout'} = "radiusSessionTimeout";
+$mapping{'idle-timeout'} = "radiusIdleTimeout";
+$mapping{'termination-action'} = "radiusTerminationAction";
+$mapping{'called-station-id'} = "radiusCalledStationId";
+$mapping{'calling-station-id'} = "radiusCallingStationId";
+$mapping{'login-lat-service'} = "radiusLoginLATService";
+$mapping{'login-lat-node'} = "radiusLoginLATNode";
+$mapping{'login-lat-group'} = "radiusLoginLATGroup";
+$mapping{'framed-appletalk-link'} = "radiusFramedAppleTalkLink";
+$mapping{'framed-appletalk-network'} = "radiusFramedAppleTalkNetwork";
+$mapping{'framed-appletalk-zone'} = "radiusFramedAppleTalkZone";
+$mapping{'port-limit'} = "radiusPortLimit";
+$mapping{'login-lat-port'} = "radiusLoginLATPort";
+
+# Must be added to rlm_ldap.c (change this to suite your needs)
+# (really not all since they are in the /etc/raddb/dictionary.compat)
+$mapping{'framed-address'} = "radiusFramedIPAddress";
+$mapping{'framed-ip-route'} = "radiusFramedRoute";
+$mapping{'framed-netmask'} = "radiusFramedIPNetmask";
+$mapping{'user-service'} = "radiusServiceType";
+# Since this might not change they could be placed in the DEFAULT
+# user insted of the LDAP
+#$mapping{'ascend-metric'} = "radiusAscendMetric";
+#$mapping{'ascend-idle-limit'} = "radiusAscendIdleLimit";
+# But this really ought to be there :
+$mapping{'callback_number'} = "radiusCallbackNumber";
+
+
+# Footer of ldif entries
+$footer = "\n\n";
+$startentry = 0;
+
+while ($line=<STDIN>) {
+ chomp $line;
+ if ( $line =~ /^[\s\t]*$/ && $startentry) {
+ $startentry = 0 ;
+ print $footer;
+ }
+ # Start line is hardcoded must be uid followed by password
+ # this could be changed to use any other parameter however
+ if ( $line =~ /^(\w+)\s*\t*(?:User-)?Password=(\w+)/ ) {
+ $uid = $1;
+ $password= $2;
+ $password = $passwords{$password} if $opt_f;
+ if ( $uid =~ /$usermatch/ ) {
+ $startentry = 1;
+ $dn=$predn.$uid.$basedn; # Start of LDIF entry
+ $header = "$dn\n";
+ push @userlist, $dn;
+ if ( $opt_m ) {
+ $header= $header."changetype: modify\n";
+ } else {
+ for (my $i=0; $i < $#objectClass+1; $i++) {
+ $header = $header."objectclass: ".$objectClass[$i]."\n";
+ }
+ }
+ print $header if !$opt_m;
+ print_entry ("cn",$uid);
+ print_entry ("sn",$uid);
+ # The following might be necessary (depending on the groups)
+ # of the object
+ #print "replace: uid\n" if $opt_m;
+ #print "uid: $uid\n";
+ #print "replace: givenname\n" if $opt_m;
+ #print "givenname: $uid\n";
+ print_entry ($mapping{'password'},$password);
+ }
+ }
+ # Do this only for entries detected
+ if ( $startentry && ! $opt_p ) {
+ #Take anything that starts with a tab or spaces
+ # and ends (sometimes) with a comma
+ if ( $line =~ /^[\t\s]+(.*?)\s+=\s+(.*?),*$/ ) {
+ $parameter = lc $1;
+ $value = $2;
+ print "DEBUG: Got :$parameter=$value\n" if $debug;
+ if ( defined $mapping{$parameter} && $mapping{$parameter} ne "" ) {
+ print_entry ($mapping{$parameter},$value);
+ } # of if defined mapping
+ else {
+ print "DEBUG: Parameter $parameter not known\n" if $debug;
+ }
+ } # of if line
+ } # of if startentry
+
+} # of while
+
+
+# The list of users in the group
+if ( $group ) {
+ if ( ! $opt_m ) {
+ print "$addgroup\n";
+ }
+ else {
+ print "\n\n$group\n";
+ print "changetype: modify\n" ;
+ }
+ foreach $user ( @userlist ) {
+ $member = "member: ";
+ $member = "uniquemember: " if $uniquemembers;
+ print "$member$user\n";
+ }
+}
+
+exit 0;
+
+sub read_passwds {
+# Reads passwords from a file in order to get the crypted
+# version, the file must be of the following format:
+# password cryptedversion
+ my ($file)=@_;
+ open (PASSWD,"< $file") or die ("Could not open $file: $!\n");
+
+ while ($line = <PASSWD>) {
+ chomp $line;
+ if ( $line =~ /^(\w+)[\t\s]+(.*?)$/ ) {
+ $passwords{$1}=$2;
+ }
+ }
+ close PASSWD;
+
+ return 0;
+}
+
+sub print_entry {
+# Prints and ldif entry given name and value
+# if this is a modification it will print header and footer
+ my ($name, $value) = @_;
+ print $header."replace: $name\n" if $opt_m;
+ print $name.": ".$value."\n";
+ print $footer if $opt_m;
+ return 0;
+}
+
diff --git a/scripts/ldap/schema_to_samba.py b/scripts/ldap/schema_to_samba.py
new file mode 100644
index 0000000..637ae52
--- /dev/null
+++ b/scripts/ldap/schema_to_samba.py
@@ -0,0 +1,132 @@
+# This is a quick hack to convert an openldap schema file to a form which
+# can be loaded into Samba4/AD.
+#
+# Inspired by:
+# http://david-latham.blogspot.co.uk/2012/12/extending-ad-schema-on-samba4-part-2.html
+# https://github.com/linuxplayground/yubikey-ldap/tree/master/samba4-schema
+#
+# (c) 2017 Brian Candler <b.candler@pobox.com>
+# -------------------------------------------------------------------------
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+# -----------------------------------------------------------------------
+
+from __future__ import print_function
+import sys
+import re
+from collections import OrderedDict
+
+BASEDN = 'dc=samba4,dc=internal'
+
+# RFC 2252 to https://technet.microsoft.com/en-us/library/cc961740.aspx
+SYNTAX_MAP = {
+ '1.3.6.1.4.1.1466.115.121.1.7': ('2.5.5.8', 1), # boolean
+ '1.3.6.1.4.1.1466.115.121.1.12': ('2.5.5.1', 127), # DN
+ '1.3.6.1.4.1.1466.115.121.1.15': ('2.5.5.3', 27), # DirectoryString
+ '1.3.6.1.4.1.1466.115.121.1.26': ('2.5.5.5', 22), # IA5String
+ '1.3.6.1.4.1.1466.115.121.1.27': ('2.5.5.9', 10), # Integer
+}
+obj = None
+for line in sys.stdin:
+ if re.match(r'^\s*(#|$)', line): continue
+ m = re.match(r'^attributetype\s+\(\s+(\S+)', line)
+ if m:
+ obj = OrderedDict([
+ ('objectClass', ['top', 'attributeSchema']),
+ ('attributeID', m.group(1)),
+ ('isSingleValued', 'FALSE'),
+ ])
+ continue
+ m = re.match(r'^objectclass\s+\(\s+(\S+)', line)
+ if m:
+ obj = OrderedDict([
+ ('objectClass', ['top', 'classSchema']),
+ ('governsID', m.group(1)),
+ ])
+ continue
+ m = re.match(r'^\s*NAME\s+[\'"](.+)[\'"]', line)
+ if m:
+ obj.update([
+ ('cn', m.group(1)),
+ ('name', m.group(1)),
+ ('lDAPDisplayName', m.group(1)),
+ ])
+ continue
+ m = re.match(r'^\s*DESC\s+[\'"](.+)[\'"]', line)
+ if m:
+ obj.update([
+ ('description', m.group(1)),
+ ])
+ continue
+ m = re.match(r'^\s*(EQUALITY|SUBSTR)\s+(\S+)', line)
+ if m:
+ # Not supported by AD?
+ # https://technet.microsoft.com/en-us/library/cc961575.aspx
+ continue
+ m = re.match(r'^\s*SYNTAX\s+(\S+)', line)
+ if m:
+ obj.update([
+ ('attributeSyntax', SYNTAX_MAP[m.group(1)][0]),
+ ('oMSyntax', SYNTAX_MAP[m.group(1)][1]),
+ ])
+ continue
+ if re.match(r'^\s*SINGLE-VALUE', line):
+ obj.update([
+ ('isSingleValued', 'TRUE'),
+ ])
+ continue
+ if re.match(r'^\s*AUXILIARY', line):
+ # https://msdn.microsoft.com/en-us/library/ms679014(v=vs.85).aspx
+ # https://technet.microsoft.com/en-us/library/2008.05.schema.aspx
+ obj.update([
+ ('objectClassCategory', '3'),
+ ])
+ continue
+ if re.match(r'^\s*STRUCTURAL', line):
+ obj.update([
+ ('objectClassCategory', '1'),
+ ])
+ continue
+ m = re.match(r'^\s*SUP\s+(\S+)', line)
+ if m:
+ obj.update([
+ ('subClassOf', m.group(1)),
+ ])
+ continue
+ m = re.match(r'^\s*(MAY|MUST)\s+\((.*)\)\s*$', line)
+ if m:
+ attrs = m.group(2).split('$')
+ obj.update([
+ ('%sContain' % m.group(1).lower(), [v.strip() for v in attrs]),
+ ])
+ continue
+ m = re.match(r'^\s*(MAY|MUST)\s+(\w+)\s*$', line)
+ if m:
+ obj.update([
+ ('%sContain' % m.group(1).lower(), m.group(2)),
+ ])
+ continue
+ if re.match(r'^\s*\)', line) and obj:
+ print("dn: CN=%s,CN=Schema,CN=Configuration,%s" % (obj['cn'], BASEDN))
+ print("changetype: add")
+ for k in obj:
+ if type(obj[k]) == list:
+ for v in obj[k]:
+ print("%s: %s" % (k, v))
+ else:
+ print("%s: %s" % (k, obj[k]))
+ print()
+ obj = None
+ continue
+ print("??? %s" % line, file=sys.stderr)