package org.openslx.taskmanager.tasks; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Random; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.openslx.satserver.util.LdapMapping; import org.openslx.satserver.util.Util; import org.openslx.taskmanager.api.SystemCommandTask; import com.google.gson.annotations.Expose; public class LdapSearch extends SystemCommandTask { private static final Logger LOGGER = LogManager.getLogger( LdapSearch.class ); @Expose private String server = null; @Expose private String searchbase = null; @Expose private String binddn = null; @Expose private String bindpw = null; @Expose private boolean plainldap = false; @Expose private String filter = null; @Expose private LdapMapping mapping = null; private String fifo = null; private boolean getDn = false; private volatile int userCount = 0; private volatile int userIdCount = 0; private Output status = new Output(); @Override protected boolean initTask() { this.setStatusObject( this.status ); if ( this.server == null || this.searchbase == null ) { status.messages = "Missing parameter"; return false; } if ( this.binddn == null ) this.binddn = ""; if ( this.bindpw == null ) this.bindpw = ""; this.timeoutSeconds = 5; return true; } @Override protected String[] initCommandLine() { if ( this.mapping == null ) this.mapping = new LdapMapping(); if ( this.plainldap ) { if ( Util.isEmpty( mapping.posixAccount ) ) { mapping.posixAccount = "posixAccount"; } if ( Util.isEmpty( mapping.uid ) ) { mapping.uid = "uid"; } if ( Util.isEmpty( mapping.uidnumber ) ) { mapping.uidnumber = "uidnumber"; } } else { if ( Util.isEmpty( mapping.posixAccount ) ) { mapping.posixAccount = "user"; } if ( Util.isEmpty( mapping.uid ) ) { mapping.uid = "sAMAccountName"; } if ( Util.isEmpty( mapping.uidnumber ) ) { mapping.uidnumber = "objectSid"; } } mapping.uid = mapping.uid.toLowerCase(); mapping.uidnumber = mapping.uidnumber.toLowerCase(); if ( !this.bindpw.isEmpty() ) { this.fifo = String.format( "/tmp/bwlp-%s-%s.ldap", System.currentTimeMillis(), new Random().nextInt() ); File pwFile = new File( this.fifo ); FileUtils.deleteQuietly( pwFile ); try { pwFile.createNewFile(); pwFile.setReadable( false, false ); pwFile.setReadable( true, true ); FileUtils.writeStringToFile( pwFile, this.bindpw, StandardCharsets.UTF_8 ); } catch ( IOException e ) { FileUtils.deleteQuietly( pwFile ); status.messages = e.toString(); LOGGER.warn( "Cannot create pwfile", e ); return null; } } String filter; if ( this.filter == null || this.filter.isEmpty() ) { status.addMessage( "Trying to find 4 random users to verify everything is all right..." ); filter = "(&(objectClass=" + mapping.posixAccount + ")(" + mapping.uid + "=*)(" + mapping.uidnumber + "=*))"; } else { this.getDn = true; filter = this.filter; } status.addMessage( "Using filter: " + filter ); // As we don't care about the certificate here, you might want to put TLS_REQCERT never // in /etc/ldap/ldap.conf if ( this.bindpw.isEmpty() ) { return new String[] { "ldapsearch", "-x", // Simple auth "-LLL", // No additional stuff "-H", this.server, // Host "-b", this.searchbase, // SB "-D", this.binddn, // DN "-l", "4", // Time limit in seconds "-o", "nettimeout=4", "-z", "4", // Max number of results "-o", "ldif-wrap=no", // Turn off retarded line wrapping done by ldapsearch filter }; } return new String[] { "ldapsearch", "-x", // Simple auth "-LLL", // No additional stuff "-y", this.fifo, // Password from file "-H", this.server, // Host "-b", this.searchbase, // SB "-D", this.binddn, // DN "-l", "4", // Time limit in seconds "-o", "nettimeout=4", "-z", "4", // Max number of results "-o", "ldif-wrap=no", // Turn off retarded line wrapping done by ldapsearch filter }; } @Override protected boolean processEnded( int exitCode ) { if ( this.fifo != null ) { FileUtils.deleteQuietly( new File( this.fifo ) ); } if ( exitCode == 4 ) // Means size limit exceeded, ignore exitCode = 0; status.addMessage( "Exit code is " + exitCode ); if ( exitCode == 0 && !this.getDn ) { if ( this.userCount < 4 ) { status.addMessage( "Found less than 4 users. Are you sure you got the right credentials?" ); } if ( this.userIdCount < 4 ) { status.addMessage( "Found less than 4 user ids. Are you sure you got the right credentials?" ); } } return ( this.userCount >= 4 && this.userIdCount >= 4 ) || ( this.getDn && status.dn != null ); } @Override protected void processStdOut( String line ) { String lower = line.toLowerCase(); if ( lower.startsWith( mapping.uid + ":" ) ) { status.addMessage( "Found " + line + " :-)" ); this.userCount++; return; } if ( lower.startsWith( mapping.uidnumber + ":" ) ) { status.addMessage( "Found " + line + " :-)" ); this.userIdCount++; return; } if ( lower.startsWith( "dn: " ) ) { status.dn = line.substring( 4 ); return; } // Figure out if we have homedir if ( this.getDn ) { String p[] = line.split( ": ", 2 ); if ( p.length == 2 ) { int score = 0; if ( p[1].startsWith( "\\\\" ) ) { score += 10; } if ( p[0].equalsIgnoreCase( "homeDirectory" ) ) { score += 60; } else if ( p[0].contains( "homeDirectory" ) ) { score += 50; } else if ( p[0].contains( "homedirectory" ) ) { score += 40; } else if ( p[0].contains( "home" ) ) { score += 10; } if ( p[0].contains( "user" ) ) { score += 10; } if ( score > 10 ) { status.addMessage( "Potential home directory attribute: " + p[0] ); status.home.add( new DirCandidate( p[0], p[1], score ) ); return; } } } } @Override protected void processStdErr( String line ) { if ( line.contains( "Size limit exceeded" ) ) return; status.addMessage( "Error: " + line ); } class DirCandidate { public String attr; public String value; public int score; public DirCandidate( String attr, String value, int score ) { this.attr = attr; this.value = value; this.score = score; } } class Output { private String messages = null; public String dn = null; public List home = new ArrayList<>(); private synchronized void addMessage( String str ) { if ( messages == null ) { messages = str; } else { messages += "\n" + str; } } } }