I have two strings representing a user and a password for that user. I'd like to check if this is a valid authentication pair in a Unix environment.
I thought about running a sudo bash command to authenticate inside Java code and see if the exit value of the command executed is equal to 0.
However I cannot make this work.
public class Test{
public static void main(String args[]){
String user = "user";
String pass = "pass\n";
try{
Process proc = new ProcessBuilder(
"/bin/sh","-c","sudo","-S","su",user).start();
OutputStream os = proc.getOutputStream();
os.write(pass.getBytes());
os.flush();
os.close();
try{
System.out.println(proc.waitFor());
} catch ( InterruptedException e ){
e.printStackTrace();
}
} catch ( IOException e ){
e.printStackTrace();
}
}
}
This code prints 1 as exit code.
I have really tried everything I could and found on the net but I still have to figure out how to make this work properly.
Can anybody help me?
Thx
sudo asks for the password of the current user, that is, the user that started the java process, so it will not check against user's password.
Instead of "/bin/sh","-c","sudo","-S","su",user).start();try "/bin/sh","-c","su",user).start();. This will just attempt to switch to that user, and as such will ask for user's password.
Based on su.c source code I've wrote a simple java program that makes the user credentials verification using JNA. It must works on all Unix based distributions that have the libc and crypt libraries, bellow a code snippet:
public static void main(String[] args) {
final Scanner scanner = new Scanner(System.in);
System.out.println("type the user");
final String user = scanner.nextLine();
System.out.println("type password");
final String password = scanner.nextLine();
System.out.println("RESULT\n===========================================");
final SPassword passwd = CLibrary.INSTANCE.getspnam(user);
if(passwd == null){
throw new RuntimeException(String.valueOf(Native.getLastError()));
}
final String encrypted = Crypt.INSTANCE.crypt(password, passwd.sp_pwdp);
System.out.printf("matches=%b%n", encrypted.equals(passwd.sp_pwdp));
}
interface Crypt extends Library {
Crypt INSTANCE = Native.loadLibrary("crypt", Crypt.class);
String crypt(String key, String salt);
}
interface CLibrary extends Library {
CLibrary INSTANCE = Native.loadLibrary("c", CLibrary.class);
Password getpwnam(String username);
SPassword getspnam(String username);
}
Testing
git clone https://github.com/mageddo/java-native-examples.git &&\
cd java-native-examples && git checkout -f ef4eb3e &&\
./gradlew clean build fatjar &&\
sudo java -jar build/libs/java-native-examples-all-*.jar
out
type the user
elvis
type password
*********
RESULT
===========================================
matches=true
Obs: The drawback is this application must run as root or sudo user, I can't fix that yet, anyway I think it is possible once su command don't need, for security reasons(if it is a problem in your context) I suggest to isolate this functionality to a application then call it by REST, Soap, TCP, whatever. this way your current application will not need to run as root.
If you want you can replace crypt function by apache commons codec lib function that do the same thing.
Reference
Function to recover user credentials
Lib for encrypt password in Linux format
Related
I'm using ProcessBuilder to execute bash commands:
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
Process pb = new ProcessBuilder("gedit").start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
But I want to make something like this:
Process pb = new ProcessBuilder("sudo", "gedit").start();
How to pass superuser password to bash?
("gksudo", "gedit") will not do the trick, because it was deleted since Ubuntu 13.04 and I need to do this with available by default commands.
EDIT
gksudo came back to Ubuntu 13.04 with the last update.
I think you can use this, but I'm a bit hesitant to post it. So I'll just say:
Use this at your own risk, not recommended, don't sue me, etc...
public static void main(String[] args) throws IOException {
String[] cmd = {"/bin/bash","-c","echo password| sudo -S ls"};
Process pb = Runtime.getRuntime().exec(cmd);
String line;
BufferedReader input = new BufferedReader(new InputStreamReader(pb.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
}
Edit /etc/sudoers with visudo and grant your user a NOPASSWD right for a specific script:
username ALL=(ALL) NOPASSWD: /opt/yourscript.sh
My solution, doesn't exposes the password in the command line, it just feed the password to the output stream of the process. This is a more flexible solution because allows you to request the password to the user when it is needed.
public static boolean runWithPrivileges() {
InputStreamReader input;
OutputStreamWriter output;
try {
//Create the process and start it.
Process pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "/usr/bin/sudo -S /bin/cat /etc/sudoers 2>&1"}).start();
output = new OutputStreamWriter(pb.getOutputStream());
input = new InputStreamReader(pb.getInputStream());
int bytes, tryies = 0;
char buffer[] = new char[1024];
while ((bytes = input.read(buffer, 0, 1024)) != -1) {
if(bytes == 0)
continue;
//Output the data to console, for debug purposes
String data = String.valueOf(buffer, 0, bytes);
System.out.println(data);
// Check for password request
if (data.contains("[sudo] password")) {
// Here you can request the password to user using JOPtionPane or System.console().readPassword();
// I'm just hard coding the password, but in real it's not good.
char password[] = new char[]{'t','e','s','t'};
output.write(password);
output.write('\n');
output.flush();
// erase password data, to avoid security issues.
Arrays.fill(password, '\0');
tryies++;
}
}
return tryies < 3;
} catch (IOException ex) {
}
return false;
}
Do not try to write a system password plainly in a file, especially for a user that have the sudo privilege, just as #jointEffort answered, issued privilege should be solved by system administrators not by app writers.
sudo allow you to grant privileges for specific command to specific user, which is precisely enough, check this post
and you can choose to manage the privilege in a separated file other than the main sudoers file if you want just append #includedirs /etc/sudoers.d/ in the main /etc/sudoers file(most Linux distributions have already done that) and make a file like ifconfig-user with:
USER_NAME ALL=(ALL) NOPASSWD: /sbin/ifconfig
Another thing, remember to edit the config file with visudo in case you lost control of your system when there is syntax error.
I know this is an old thread but i just want to put this here:
you can use sudo -S *command* as a command that you pass to create the Process instance. Then get the output stream and write the password to it, and add at the end of it a new line and a c. return (\n\r). The return may not be required but i passed it just in case. Also it is a good idea to flush the stream, to make sure everything is written to it. I've done it a few times and it works like a charm. And DO NOT forget to close streams :).
Once you spawn a process you can extract the input and output streams. Just feed the password to the output stream (you output it into the proccess's input). So the code would look something like -
Process pb = new ProcessBuilder("gedit").start();
OutputStream out = pb.getOutputStream();
out.write(password);
I am working on application which doesn't have any login mechanism, any user in my organization can use that. But I want to pick the username of the remote users who will use my tool. I have a button clicking on that I want to get their usernames.
I tried request.getRemoteUser got null. tried System.getenv("USERNAME") getting the logged in user of the localhost where the server resides. Tried getHostName, System.getProperty got the localhost name. Tried this also - new com.sun.security.auth.module.NTSystem().getName() but same result.
I am using java6, windows server and glassfish3 server.
Please suggest something as I don't want to use any external link and tool.
You want to do something called SSO (Single Sign On): A user is logged in somewhere (in your case his Windows computer) and you want to authenticate the user with this (already done) login. This is a very common use case and there are different ways to do that. However, the big question is always how you can trust those third party system. And this is where the trouble begins.
Since your question is not very clear, I assume you have a Java Glassfish server running on Windows Server and a Java client (because you asked for Java code). So the Java server must authenticate who the user of the Java client is. And the server must trust this information.
Using System.getProperty("user.name"); isn't a good idea since anybody can change it. You can start your Java program with java -Duser.name=Joe <your_program> and that's it.
But since you are on Windows, you could use Windows to help you. If both, your client and server, are in the same domain, they are authenticated against the same system. You can ask this system for the user identity. Typically machines of a company are in the same domain.
To do this there is a tool called Waffle. It does a secure Windows authentication between machines in the same domain. If your client and server are in the same domain, it is an easy way to perform an SSO (a single sign on). You can find it on GitHub: http://dblock.github.io/waffle/
Here is a simple example from one of my own questions a couple of months ago (see here):
// client credentials handle
IWindowsCredentialsHandle credentials= WindowsCredentialsHandleImpl.getCurrent("Negotiate");
credentials.initialize();
// initial client security context
WindowsSecurityContextImpl clientContext = new WindowsSecurityContextImpl();
clientContext.setPrincipalName(Advapi32Util.getUserName());
clientContext.setCredentialsHandle(credentials.getHandle());
clientContext.setSecurityPackage(securityPackage);
clientContext.initialize();
// accept on the server
WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
IWindowsSecurityContext serverContext = null;
do {
if (serverContext != null) {
// initialize on the client
SecBufferDesc continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, serverContext.getToken());
clientContext.initialize(clientContext.getHandle(), continueToken);
}
// accept the token on the server
serverContext = provider.acceptSecurityToken(clientContext.getToken(), "Negotiate");
} while (clientContext.getContinue() || serverContext.getContinue());
System.out.println(serverContext.getIdentity().getFqn());
for (IWindowsAccount group : serverContext.getIdentity().getGroups())
System.out.println(" " + group.getFqn());
You can use Waffle also for websites. However, I didn't do that and cannot explain you what to do in this case.
And one important remark: I think you are a little bit confused. If you do request.getRemoteHost() on your server, you try to get the identity of the client who send the request (by the way, it is not secure, a client could send anything). However, if you do System.getProperty("user.name") on your server, you try to get the name of the server itself. Be aware where you are (on client or server) and what you want. And make sure whether you can trust this information or not. Security is difficult.
java class code to find who loggedin into a remote computer in a domain
package com.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import com.test.Pubfun;
public class UserName {
public static HashMap <String,String> hmun=new HashMap<String, String>();
public String setUserFromIP(String arg1) {
String m = arg1;
StringBuilder user = new StringBuilder();
String u = "";
String user2 = null;
try {
Process p = Runtime.getRuntime().exec("query user /server:" + m);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line = reader.readLine();
while (line != null) {
line = reader.readLine();
user.append(line);
line=null;
}
} catch (IOException e1) {
} catch (InterruptedException e2) {
}
u = user.toString().replace("null", "");
try {
user2 = this.getUserFromString(u);
} catch (ArrayIndexOutOfBoundsException ae) {
}
u.replace("null", " ");
System.out.println(user2);
hmun.put("username",user2);
return user2;
}
public static String gethmun()
{
String t=hmun.get("username");
return t;
}
public String getUserFromString(String u) {
HashMap <String,String> hmun=new HashMap<String, String>();
String input = u;
int length, size;
length = input.length();
size = length ;
String strarray[] = new String[size];
strarray = input.split("\\s+");
for (int i = 0; i < strarray.length; i++) {
if(strarray[i].equals("Active")){
hmun.put("username", strarray[i-3]);
}
}
String user1=hmun.get("username");
return user1;
}
}
HttpServletRequest.getRemoteUser() might optionally return the login of user making the request (if authenticated), but it is not the username of the user logged in on the remote machine.
There is no way to query the username of the remote machine. Browsers or applications making the requests might send this info voluntarily, but if they don't, you won't find a way to get it. And by default they don't send it so don't count on this.
This gives you the current logged in Username from your local Windows System System.getProperty("user.name");
I've got this going at the moment;
public class ProcessKill {
private static final String TASKLIST = "tasklist";
private static final String KILL = "taskkill /IM ";
public static boolean isProcessRunging(String serviceName) throws Exception {
Process p = Runtime.getRuntime().exec(TASKLIST);
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line;
while ((line = reader.readLine()) != null){
System.out.println(line);
if (line.contains(serviceName)){
return true;
}
}
return false;
}
public static void killProcess(String serviceName) throws Exception {
Runtime.getRuntime().exec(KILL + serviceName);
}
}
public static void main(String args[]) throws Exception{
ProcessKill pkill = new ProcessKill();
String processName = "wmplayer.exe";
if (pkill.isProcessRunging(processName)){
pkill.killProcess(processName);
}
}
Only problem is, what if the name is changed? I don't want to keep updating the code. Is there a way to grab a hex value of an .exe file and read it into Java for it to detect that instead?
Thanks
well, you can first check if the process you want to kill is running by executing wmic.exe PROCESS where name='"+processName+"' - although WMIC provides a PROCESS DELETE option, the java-process blocks when calling this. wmic PROCESS (without any specifig process name) returns a list (and description) of all currently running processes on a windows machine which you can read in using an InputSreamReader() and then iterate through all running processes and check which of these might be your target process. Moreover the line of your target process contains arguments passed to your process which you can either use to distinguish processes in case you have multiple processes running or need to delete some resource files your application used.
While debugging our application which uses an embedded mongodb for unit-tests, after debugging the embedded mongod process kept running and refused to work on the next unit-test (as the unit-test cleanup was not executed properly) - furthermore it created a local datastore which could not be overridden by a new embedded mongod process - so it needed to get deleted manually. I therefore extracted the file from the line returned by WMIC and deleted the files after killing the process automatically.
As far as I understand you do not want to hold the process name hard-coded. In this case just pass it through command line parameters:
public static void main(String args[]) throws Exception{
ProcessKill pkill = new ProcessKill();
String processName = args[0];
if (pkill.isProcessRunging(processName)){
pkill.killProcess(processName);
}
}
Now your user has to provide the process name to kill in command line:
:> ProcessKill notepad.exe
However IMHO I do not understand why do you need your utility at all? Indeed you just run processkill, so user can use it directly. Your utility does not provide any additional functionality.
I'm using ProcessBuilder to execute bash commands:
import java.io.IOException;
public class Main {
public static void main(String[] args) {
try {
Process pb = new ProcessBuilder("gedit").start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
But I want to make something like this:
Process pb = new ProcessBuilder("sudo", "gedit").start();
How to pass superuser password to bash?
("gksudo", "gedit") will not do the trick, because it was deleted since Ubuntu 13.04 and I need to do this with available by default commands.
EDIT
gksudo came back to Ubuntu 13.04 with the last update.
I think you can use this, but I'm a bit hesitant to post it. So I'll just say:
Use this at your own risk, not recommended, don't sue me, etc...
public static void main(String[] args) throws IOException {
String[] cmd = {"/bin/bash","-c","echo password| sudo -S ls"};
Process pb = Runtime.getRuntime().exec(cmd);
String line;
BufferedReader input = new BufferedReader(new InputStreamReader(pb.getInputStream()));
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
}
Edit /etc/sudoers with visudo and grant your user a NOPASSWD right for a specific script:
username ALL=(ALL) NOPASSWD: /opt/yourscript.sh
My solution, doesn't exposes the password in the command line, it just feed the password to the output stream of the process. This is a more flexible solution because allows you to request the password to the user when it is needed.
public static boolean runWithPrivileges() {
InputStreamReader input;
OutputStreamWriter output;
try {
//Create the process and start it.
Process pb = new ProcessBuilder(new String[]{"/bin/bash", "-c", "/usr/bin/sudo -S /bin/cat /etc/sudoers 2>&1"}).start();
output = new OutputStreamWriter(pb.getOutputStream());
input = new InputStreamReader(pb.getInputStream());
int bytes, tryies = 0;
char buffer[] = new char[1024];
while ((bytes = input.read(buffer, 0, 1024)) != -1) {
if(bytes == 0)
continue;
//Output the data to console, for debug purposes
String data = String.valueOf(buffer, 0, bytes);
System.out.println(data);
// Check for password request
if (data.contains("[sudo] password")) {
// Here you can request the password to user using JOPtionPane or System.console().readPassword();
// I'm just hard coding the password, but in real it's not good.
char password[] = new char[]{'t','e','s','t'};
output.write(password);
output.write('\n');
output.flush();
// erase password data, to avoid security issues.
Arrays.fill(password, '\0');
tryies++;
}
}
return tryies < 3;
} catch (IOException ex) {
}
return false;
}
Do not try to write a system password plainly in a file, especially for a user that have the sudo privilege, just as #jointEffort answered, issued privilege should be solved by system administrators not by app writers.
sudo allow you to grant privileges for specific command to specific user, which is precisely enough, check this post
and you can choose to manage the privilege in a separated file other than the main sudoers file if you want just append #includedirs /etc/sudoers.d/ in the main /etc/sudoers file(most Linux distributions have already done that) and make a file like ifconfig-user with:
USER_NAME ALL=(ALL) NOPASSWD: /sbin/ifconfig
Another thing, remember to edit the config file with visudo in case you lost control of your system when there is syntax error.
I know this is an old thread but i just want to put this here:
you can use sudo -S *command* as a command that you pass to create the Process instance. Then get the output stream and write the password to it, and add at the end of it a new line and a c. return (\n\r). The return may not be required but i passed it just in case. Also it is a good idea to flush the stream, to make sure everything is written to it. I've done it a few times and it works like a charm. And DO NOT forget to close streams :).
Once you spawn a process you can extract the input and output streams. Just feed the password to the output stream (you output it into the proccess's input). So the code would look something like -
Process pb = new ProcessBuilder("gedit").start();
OutputStream out = pb.getOutputStream();
out.write(password);
How can I get the serial number of the hard disk with Java?
Java runs on a virtual machine which doesn't have hard drives only files and filesystems. You should be able to get this information by running the approriate command line utility from Java.
One Linux you can do
hdparm -i /dev/hda
Good news there is a volume:vsn attribute for the FileStore object.
for (FileStore store: FileSystems.getDefault().getFileStores()) {
System.out.format("%-20s vsn:%s\n", store, store.getAttribute("volume:vsn"));
}
The output looks like an int (e.g. -1037833820, 194154)
Bad news it is windows specific for testing purposes as they say in WindowsFileStore jdk8u source. Might work in other platforms and in the future, or not.
Other options (JNI, executing and parsing CLI) are also system dependent. Are more ellaborate but might not suddenly break up when you change the runtime. But I think the attribute solution is much simpler and I am totally going with it until I have something better.
If you are using a Windows OS which is Win7 SP1 or higher, you can simply get it by executing the following cmd command in your java program, via wmic.
wmic diskdrive get serialnumber
If this command returns "Invalid XML content." you may wanna apply the hotfix file as described in here.
on windows machine you can use
public static String getSerialKey(Character letter) throws Exception{
String line = null;
String serial = null;
Process process = Runtime.getRuntime().exec("cmd /c vol "+letter+":");
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()) );
while ((line = in.readLine()) != null) {
if(line.toLowerCase().contains("serial number")){
String[] strings = line.split(" ");
serial = strings[strings.length-1];
}
}
in.close();
return serial;
}
Windows: This is a windows dependent solution using Java which invokes VBS.
Linux: try this c program and use JNI for Java.
I would imagine you'd have to implement that feature in C or C++ and use JNI to access it from Java.
For a Windows machine you can execute wmic with parameters and than parse the result like is done in the following snippet:
public static String getNumeroDisq() throws IOException {
Runtime runtime = Runtime.getRuntime();
String[] commands = {"wmic", "diskdrive", "get", "SerialNumber"};
Process process = runtime.exec(commands);
String chain = null;
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String serialNumber= null;
while ((serialNumber = bufferedReader.readLine()) != null) {
if (serialNumber.trim().length() > 0) {
chain = serialNumber;
}
}
return chain.trim();
}
}