New build path variable
[freeradius.git] / scripts / radiusd2ldif.pl
1 #!/usr/bin/perl
2
3 # radius2ldif.pl
4 #
5 #        To test this program, do the following
6 #Take a radius users' file, for example with:
7 #
8 #myuser Password = "apassword"
9 #        User-Service = Framed-User,
10 #        Framed-Protocol = PPP,
11 #        Framed-Address = 255.255.255.255,
12 #        Framed-Netmask = 255.255.255.255,
13 #        Ascend-Metric = 2,
14 #        Framed-Routing = None,
15 #        Framed-Compression = 0,
16 #        Ascend-Idle-Limit = 0,
17 #        Ascend-Maximum-Time = 36000
18 #
19 #and do:
20 #
21 #cat users | ./radius2ldif
22 #
23 #Output is:
24 #dn: cn=myuser, ou=Hardware, ou=EDUCAMADRID, ou=People, o=icm.es
25 #objectclass: top
26 #objectclass: person
27 #objectclass: radiusprofile
28 #cn: myuser
29 #sn: myuser
30 #userpassword: apassword
31 #radiusServiceType: Framed-User
32 #radiusFramedProtocol: PPP
33 #radiusFramedIPAddress: 255.255.255.255
34 #radiusFramedIPNetmask: 255.255.255.255
35 #radiusFramedRouting: None
36 #radiusFramedCompression: 0
37 #
38 #dn: ou=RadiusUser, ou=Groups, o=icm.es
39 #description: RadiusUser
40 #objectclass: top
41 #objectclass: groupOfUniqueNames
42 #cn: RadiusUser
43 #uniquemember: dn: cn=myuser, ou=Hardware, ou=EDUCAMADRID, ou=People, o=icm.es
44 #
45 # (c) 2000 Javier Fern'andez-Sanguino Pen~a  <jfs@computer.org>
46 # -------------------------------------------------------------------------
47 #    This program is free software; you can redistribute it and/or modify
48 #    it under the terms of the GNU General Public License as published by
49 #    the Free Software Foundation; either version 2 of the License, or
50 #    (at your option) any later version.
51 #
52 #    This program is distributed in the hope that it will be useful,
53 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
54 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
55 #    GNU General Public License for more details.
56 #
57 #    You should have received a copy of the GNU General Public License
58 #    along with this program; if not, write to the Free Software
59 #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
60 # -----------------------------------------------------------------------
61
62
63 # TODO:
64 # currently does not encrypt passwords (takes them from outside file)
65
66 # Command line options
67 # -d : debugging output
68 # -p : give only password
69 # -m : set entry to modify ldap attributes
70 # -f : read encrypted passwords from file
71 use Getopt::Std;
72 getopts('dpmf:');
73 $debug = $opt_d;
74
75 %passwords;
76 # This might or might not be necessary depending if your LDAP server
77 # when importing from ldif introduces crypted passwords in the LDAP db
78 # (not necessary for Netscape's Directory Server)
79 read_passwds ($opt_f) if $opt_f;
80
81 # USER CONFIGURATION
82 # ------------------
83 $usermatch = ".*"; # only add users matching this
84 # WARNING: in order to add  *all* users set this to ".*" NOT ""
85
86 # LDAP configuration
87 $domain = "o=icm.es";
88 $basedn = ", ou=Hardware, ou=EDUCAMADRID, ou=People, $domain";
89 $predn = "dn: cn=";
90 $uniquemembers = 1;
91 $groupname = "RadiusUser"; # group to add in the LDAP, if null will not add
92 $group = "\n\ndn: ou=$groupname, ou=Groups, $domain";
93 # Only useful for adding the group (not yet implemented)
94 $addgroup = $group."\ndescription: $groupname\nobjectclass: top";
95 if ( $uniquemembers ) {
96 $addgroup = $addgroup."\nobjectclass: groupOfUniqueNames";
97 } else {
98 $addgroup = $addgroup."\nobjectclass: groupOfNames";
99 }
100 $addgroup = $addgroup."\ncn: $groupname";
101 # The following group must be created first
102 # (ldif entry), the script will *not* create it
103 #cn=$group,ou=Groups,o=icm.es
104 #description=whatever
105 #objectclass=top
106 #objectclass=groupOfUniqueNames
107 # (or objectclass=groupOfNames)
108 #cn=$group
109 # Required: person (for userpasswords) and radiusprofile (<draft-aboba-radius-02.txt> 5 February 1998)
110 @objectClass = ( "top", "person" , "radiusprofile" );
111
112
113 # Mapping of entries (use lower case so no check needs to be make)
114 # From freeradius: rlm_ldap.c
115 #        { "radiusServiceType", "Service-Type" },
116 #        { "radiusFramedProtocol", "Framed-Protocol" },
117 #        { "radiusFramedIPAddress", "Framed-IP-Address" },
118 #        { "radiusFramedIPNetmask", "Framed-IP-Netmask" },
119 #        { "radiusFramedRoute", "Framed-Route" },
120 #        { "radiusFramedRouting", "Framed-Routing" },
121 #        { "radiusFilterId", "Filter-Id" },
122 #        { "radiusFramedMTU", "Framed-MTU" },
123 #        { "radiusFramedCompression", "Framed-Compression" },
124 #        { "radiusLoginIPHost", "Login-IP-Host" },
125 #        { "radiusLoginService", "Login-Service" },
126 #        { "radiusLoginTCPPort", "Login-TCP-Port" },
127 #        { "radiusCallbackNumber", "Callback-Number" },
128 #        { "radiusCallbackId", "Callback-Id" },
129 #        { "radiusFramedRoute", "Framed-Route" },
130 #        { "radiusFramedIPXNetwork", "Framed-IPX-Network" },
131 #        { "radiusClass", "Class" },
132 #        { "radiusSessionTimeout", "Session-Timeout" },
133 #        { "radiusIdleTimeout", "Idle-Timeout" },
134 #        { "radiusTerminationAction", "Termination-Action" },
135 #        { "radiusCalledStationId", "Called-Station-Id" },
136 #        { "radiusCallingStationId", "Calling-Station-Id" },
137 #        { "radiusLoginLATService", "Login-LAT-Service" },
138 #        { "radiusLoginLATNode", "Login-LAT-Node" },
139 #        { "radiusLoginLATGroup", "Login-LAT-Group" },
140 #        { "radiusFramedAppleTalkLink", "Framed-AppleTalk-Link" },
141 #        { "radiusFramedAppleTalkNetwork", "Framed-AppleTalk-Network" },
142 #        { "radiusFramedAppleTalkZone", "Framed-AppleTalk-Zone" },
143 #        { "radiusPortLimit", "Port-Limit" },
144 #        { "radiusLoginLATPort", "Login-LAT-Port" },
145 # You can change to the mappings below like this
146 # cat radius2ldif.pl | grep ^# | \
147 # perl -ne 'if ( /\{ \"(.*?)\", \"(.*?)\" \}/ ) \
148 # { $attr=lc $2; print "\$mapping{\"$attr\"} = \"$1\";\n" ; } '
149
150
151 # Warning: sometimes password must be encrypted before sent to the LDAP
152 # Which Perl libraries are available? Only way I find is through
153 # Netscape's NDS getpwenc.
154 # However NDS does the cyphering even if sending plain passwords
155 # (do all LDAP's do this?)
156 # TODO: test with OpenLDAP
157 $mapping{'password'} = "userpassword";
158 $mapping{'service-type'} = "radiusServiceType";
159 $mapping{'framed-protocol'} = "radiusFramedProtocol";
160 $mapping{'framed-ip-address'} = "radiusFramedIPAddress";
161 $mapping{'framed-ip-netmask'} = "radiusFramedIPNetmask";
162 $mapping{'framed-route'} = "radiusFramedRoute";
163 $mapping{'framed-routing'} = "radiusFramedRouting";
164 $mapping{'filter-id'} = "radiusFilterId";
165 $mapping{'framed-mtu'} = "radiusFramedMTU";
166 $mapping{'framed-compression'} = "radiusFramedCompression";
167 $mapping{'login-ip-host'} = "radiusLoginIPHost";
168 $mapping{'login-service'} = "radiusLoginService";
169 $mapping{'login-tcp-port'} = "radiusLoginTCPPort";
170 $mapping{'callback-number'} = "radiusCallbackNumber";
171 $mapping{'callback-id'} = "radiusCallbackId";
172 $mapping{'framed-ipx-network'} = "radiusFramedIPXNetwork";
173 $mapping{'class'} = "radiusClass";
174 $mapping{'session-timeout'} = "radiusSessionTimeout";
175 $mapping{'idle-timeout'} = "radiusIdleTimeout";
176 $mapping{'termination-action'} = "radiusTerminationAction";
177 $mapping{'called-station-id'} = "radiusCalledStationId";
178 $mapping{'calling-station-id'} = "radiusCallingStationId";
179 $mapping{'login-lat-service'} = "radiusLoginLATService";
180 $mapping{'login-lat-node'} = "radiusLoginLATNode";
181 $mapping{'login-lat-group'} = "radiusLoginLATGroup";
182 $mapping{'framed-appletalk-link'} = "radiusFramedAppleTalkLink";
183 $mapping{'framed-appletalk-network'} = "radiusFramedAppleTalkNetwork";
184 $mapping{'framed-appletalk-zone'} = "radiusFramedAppleTalkZone";
185 $mapping{'port-limit'} = "radiusPortLimit";
186 $mapping{'login-lat-port'} = "radiusLoginLATPort";
187
188 # Must be added to rlm_ldap.c (change this to suite your needs)
189 # (really not all since they are in the /etc/raddb/dictionary.compat)
190 $mapping{'framed-address'} = "radiusFramedIPAddress";
191 $mapping{'framed-ip-route'} = "radiusFramedRoute";
192 $mapping{'framed-netmask'} = "radiusFramedIPNetmask";
193 $mapping{'user-service'} = "radiusServiceType";
194 # Since this might not change they could be placed in the DEFAULT
195 # user insted of the LDAP
196 #$mapping{'ascend-metric'} = "radiusAscendMetric";
197 #$mapping{'ascend-idle-limit'} = "radiusAscendIdleLimit";
198 # But this really ought to be there :
199 $mapping{'callback_number'} = "radiusCallbackNumber";
200
201
202 # Footer of ldif entries
203 $footer = "\n\n";
204 $startentry = 0;
205
206 while ($line=<STDIN>) {
207         chomp $line;
208         if ( $line =~ /^[\s\t]*$/  && $startentry) {
209                 $startentry = 0 ;
210                 print $footer;
211         }
212         # Start line is hardcoded must be uid followed by password
213         # this could be changed to use any other parameter however
214         if ( $line =~ /^(\w+)\s*\t*(?:User-)?Password=(\w+)/ ) {
215                 $uid = $1;
216                 $password= $2;
217                 $password = $passwords{$password} if $opt_f;
218         if ( $uid =~ /$usermatch/ ) {
219                 $startentry = 1;
220                 $dn=$predn.$uid.$basedn; # Start of LDIF entry
221                 $header = "$dn\n";
222                 push @userlist, $dn;
223                 if ( $opt_m ) {
224                         $header= $header."changetype: modify\n";
225                 } else {
226                         for (my $i=0; $i < $#objectClass+1; $i++) {
227                                 $header = $header."objectclass: ".$objectClass[$i]."\n";
228                         }
229                 }
230                 print $header if !$opt_m;
231                 print_entry ("cn",$uid);
232                 print_entry ("sn",$uid);
233                 # The following might be necessary (depending on the groups)
234                 # of the object
235                 #print "replace: uid\n" if $opt_m;
236                 #print "uid: $uid\n";
237                 #print "replace: givenname\n" if $opt_m;
238                 #print "givenname: $uid\n";
239                 print_entry ($mapping{'password'},$password);
240         }
241         }
242         # Do this only for entries detected
243         if ( $startentry  && ! $opt_p ) {
244                 #Take anything that starts with a tab or spaces
245                 # and ends (sometimes) with a comma
246                 if ( $line =~ /^[\t\s]+(.*?)\s+=\s+(.*?),*$/ ) {
247                         $parameter = lc $1;
248                         $value = $2;
249                         print "DEBUG: Got :$parameter=$value\n" if $debug;
250                         if ( defined $mapping{$parameter}  && $mapping{$parameter} ne "" ) {
251                                 print_entry ($mapping{$parameter},$value);
252                         } # of if defined mapping
253                         else {
254                                 print "DEBUG: Parameter $parameter not known\n" if $debug;
255                                 }
256                 } # of if line
257         } # of if startentry
258
259 } # of while
260
261
262 # The list of users in the group
263 if ( $group ) {
264         if ( ! $opt_m ) {
265                 print "$addgroup\n";
266         }
267         else {
268                 print "\n\n$group\n";
269                 print "changetype: modify\n" ;
270         }
271         foreach $user ( @userlist ) {
272                 $member = "member: ";
273                 $member = "uniquemember: " if $uniquemembers;
274                 print "$member$user\n";
275         }
276 }
277
278 exit 0;
279
280 sub read_passwds {
281 # Reads passwords from a file in order to get the crypted
282 # version, the file must be of the following format:
283 # password      cryptedversion
284         my ($file)=@_;
285         open (PASSWD,"< $file") or die ("Could not open $file: $!\n");
286
287         while ($line = <PASSWD>) {
288                 chomp $line;
289                 if ( $line =~ /^(\w+)[\t\s]+(.*?)$/ ) {
290                         $passwords{$1}=$2;
291                 }
292         }
293         close PASSWD;
294
295         return 0;
296 }
297
298 sub print_entry {
299 # Prints and ldif entry given name and value
300 # if this is a modification it will print header and footer
301         my ($name, $value) = @_;
302         print $header."replace: $name\n" if $opt_m;
303         print $name.": ".$value."\n";
304         print $footer if $opt_m;
305         return 0;
306 }
307