API Description
The firmware API reference LuCI using JSON-RPC.
The http request path for all APIs is /rpc.
For example, if your device's LAN IP is 192.168.8.1, all request is
Plain Text
Copy
POST /rpc HTTP/1.1
Host: 192.168.8.1
Content-Type: application/json
...
Example of HTTP request
All APIs use call as the JSON-RPC method and the specific functions are written in JSON-RPC params, except for login/logout related APIs.
For APIs that require authentication, put the sid (authentication token) in the JSON-RPC params instead and NOT in the HTTP Header
JSON
Copy
{
"jsonrpc": "2.0",
"method": "call",
"params": [
"AlVv2SolJU3jezHDpreZ5KDm8102f09q", // sid
"system", // module-name
"get_status" // function_name
],
"id": 0
}
Example of Json sent when the system/get_status API is called
The APIs does not yet support the JOSN-RPC standard feature of multiple functions in one request
Login and Use Steps
Step1: Get encryption parameters by challenge method
Call the challenge method and passing in the username to login as the username parameter. The username is fixed as root in standard firmware.
This method respond data for encryption, including:
alg
The enumeration value identifying the algorithm used for encryption.
1 → MD5
5 → SHA256
6 → SHA512
salt
the string used to generate password ciphertext.
nonce
The string used for encrypted transmission, which is randomly generated and valid for only 1000ms. This means that steps 2-4 must be completed within 1000ms, otherwise the process has to restart from step 1.
JSON
Copy
{
"jsonrpc": "2.0",
"method": "challenge",
"params": {
"username": "root"
},
"id": 0
}
Request Example
JSON
Copy
{
"jsonrpc": "2.0",
"result": {
"alg": 1,
"salt": "shGzEq91",
"nonce": "SGgyhFWf3lFrIpX2BFImBjE1gv2AKPC2"
},
"id": 0
}
Response Example
Step2: Generate ciphertext using openssl algorithm
The plaintext of your password and salt are used to generate the matching ciphertext. The algorithm is specified by alg.
The ciphertext is a standard unix-like password generated by the algorithm in openssl. It is the same as the value stored in /etc/shadow.
Bash
Copy
cipherPassword=$(openssl passwd -$alg -salt "$salt" "$password")
In some other languages, you can replace openssl with other libraries, such as :
JavaScript
Copy
const up = require('unixpass');
const cipherPassword = up.crypt(password, '$' + alg + '$' + salt + '$');
Python
Copy
from passlib.hash import md5_crypt, sha256_crypt, sha512_crypt
if alg == 1: # MD5
cipher_password = md5_crypt.using(salt=salt).hash(password)
elif alg == 5: # SHA-256
cipher_password = sha256_crypt.using(salt=salt, rounds=5000).hash(password)
elif alg == 6: # SHA-512
cipher_password = sha512_crypt.using(salt=salt, rounds=5000).hash(password)
As example, using the data from the response example in step 1, we can get the ciphertext password is $1$shGzEq91$.TLXit4pO5gGOdru0MYHz.
Step3: Generate hash values for login
The interface does not transmit the ciphertext password directly, but uses it to construct a hash value for verification.
Using a colon : as a concatenator, the username, cipherPassword, and nonce are concatenated to get the string {username}:{cipherPassword}:{nonce}. Then get the hash value by using MD5 algorithm on this string.
As example, using the data from the response example in step 1-2, we can get the string is root:$1$shGzEq91$.TLXit4pO5gGOdru0MYHz.:SGgyhFWf3lFrIpX2BFImBjE1gv2AKPC2, then get the hash value is 6e5fd33bbd77a67eb108cf0b8bbdb9be.
Step4: Get sid by login
Call the login method, passing the username as the username parameter and the hash value as the hash parameter.
This method responds with the sid used for authentication. The validity of the sid is 5 minutes and will be reset each time you call the interface with it. That is, if you don't call any interface for 5 minutes, you need to get the sid again.
JSON
Copy
{
"jsonrpc": "2.0",
"method": "login",
"params": {
"username": "root",
"hash": "6e5fd33bbd77a67eb108cf0b8bbdb9be"
},
"id": 0
}
Request Example
JSON
Copy
{
"jsonrpc": "2.0",
"result": {
"username": "root",
"sid": "AlVv2SolJU3jezHDpreZ5KDm8102f09q"
},
"id": 0
}
Response Example
Step5: Calling other APIs with sid
Now, you can use sid to call other functional APIs. The other functional APIs call the call method uniformly, passing sid, module-name, function_name and parameters in params array in that order.
Using the get_status API of the system module as an example:
JSON
Copy
{
"jsonrpc": "2.0",
"method": "call",
"params": [
"AlVv2SolJU3jezHDpreZ5KDm8102f09q", // sid
"system", // module-name
"get_status" // function_name
],
"id": 0
}
Request Example
Example
JavaScript
JavaScript
Copy
const axios = require('axios');
const up = require('unixpass');
const crypto = require('crypto');
const url = 'http://192.168.8.1/rpc';
const username = 'root';
const password = 'goodlife';
// Step1: Get encryption parameters by challenge method
axios
.post(url, {
jsonrpc: '2.0',
method: 'challenge',
params: {
username: username
},
id: 0
})
.then((response) => {
const result = response.data.result;
const alg = result.alg;
const salt = result.salt;
const nonce = result.nonce;
// Step2: Generate cipher text using openssl algorithm
const cipherPassword = up.crypt(password, '$' + alg + '$' + salt + '$');
// Step3: Generate hash values for login
const data = `${username}:${cipherPassword}:${nonce}`;
const hash_value = crypto.createHash('md5').update(data).digest('hex');
// Step4: Get sid by login
axios
.post(url, {
jsonrpc: '2.0',
method: 'login',
params: {
username: 'root',
hash: hash_value
},
id: 0
})
.then((response) => {
const sid = response.data.result.sid;
// Step5: Calling other APIs with sid
axios
.post(url, {
jsonrpc: '2.0',
method: 'call',
params: [
sid,
'system',
'get_status'
],
id: 0
})
.then((response) => {
console.log(JSON.stringify(response.data));
})
.catch((error) => {
console.error('Request Exception:', error);
});
})
.catch((error) => {
console.error('Request Exception:', error);
});
})
.catch((error) => {
console.error('Request Exception:', error);
});
Python
Python
Copy
import requests
import hashlib
from passlib.hash import md5_crypt, sha256_crypt, sha512_crypt
url = 'http://192.168.8.1/rpc'
username = 'root'
password = 'goodlife'
try:
# Step1: Get encryption parameters by challenge method
challenge_data = {
'jsonrpc': '2.0',
'method': 'challenge',
'params': {
'username': username
},
'id': 0
}
response = requests.post(url, json=challenge_data)
response.raise_for_status()
result = response.json()['result']
alg = result['alg']
salt = result['salt']
nonce = result['nonce']
# Step2: Generate cipher text using openssl algorithm
if alg == 1: # MD5
cipher_password = md5_crypt.using(salt=salt).hash(password)
elif alg == 5: # SHA-256
cipher_password = sha256_crypt.using(salt=salt, rounds=5000).hash(password)
elif alg == 6: # SHA-512
cipher_password = sha512_crypt.using(salt=salt, rounds=5000).hash(password)
else:
raise ValueError('Unsupported algorithm')
# Step3: Generate hash values for login
data = f"{username}:{cipher_password}:{nonce}"
hash = hashlib.md5(data.encode()).hexdigest()
# Step4: Get sid by login
login_data = {
'jsonrpc': '2.0',
'method': 'login',
'params': {
'username': username,
'hash': hash
},
'id': 0
}
response = requests.post(url, json=login_data)
response.raise_for_status()
result = response.json()['result']
sid = result['sid']
# Step5: Calling other APIs with sid
system_status_data = {
'jsonrpc': '2.0',
'method': 'call',
'params': [
sid,
'system',
'get_status'
],
'id': 0
}
response = requests.post(url, json=system_status_data)
response.raise_for_status()
print(response.json())
except requests.exceptions.RequestException as e:
print("Request Exception:", e)
except (KeyError, ValueError) as e:
print("Parameter Exception:", e)
except Exception as e:
print("An error has occurred:", e)
Shell
Bash
Copy
#!/bin/bash
host=$1
username=$2
password=$3
# Step1: Get encryption parameters by challenge method
challengeResponse="$(curl -k http://$host/rpc -d '{"jsonrpc":"2.0","method":"challenge","params":{"username":"'$username'"},"id":0}' 2>/dev/null)"
salt="$(echo $challengeResponse|awk -F "salt" '{print $2}'|cut -d '"' -f 3)"
nonce="$(echo $challengeResponse|awk -F "nonce" '{print $2}'|cut -d '"' -f 3)"
alg="$(echo $challengeResponse|awk -F "alg" '{print $2}'|awk -F ":|," '{print $2}')"
echo $challengeResponse
# Step2: Generate cipher text using openssl algorithm
cipherPassword=$(openssl passwd -$alg -salt "$salt" "$password")
echo $cipherPassword
# Step3: Generate hash values for login
hash=$(echo -n "$username:$cipherPassword:$nonce" | md5sum | cut -d' ' -f1)
echo $hash
# Step4: Get sid by login
loginResponse="$(curl -k http://$host/rpc -d '{"jsonrpc":"2.0","method":"login","params":{"username":"'$username'", "hash":"'$hash'"},"id":0}' 2>/dev/null)"
sid="$(echo $loginResponse|awk -F "sid" '{print $2}'|cut -d '"' -f 3)"
echo $sid
# Step5: Calling other APIs with sid
statusResponse="$(curl -k http://$host/rpc -d '{"jsonrpc":"2.0","method":"call","params":["'$sid'","system","get_status"],"id":0}' 2>/dev/null)"
echo $statusResponse
Java
Java
Copy
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.JSONObject;
import org.json.JSONArray;
import org.apache.commons.codec.digest.Crypt;
import org.apache.commons.codec.digest.DigestUtils;
public class LoginDemo {
private static final String URL = "http://192.168.8.1/rpc";
private static final String USERNAME = "root";
private static final String PASSWORD = "goodlife";
public static void main(String[] args) {
try {
// Step1: Get encryption parameters by challenge method
JSONObject challengeRequest = new JSONObject()
.put("jsonrpc", "2.0")
.put("method", "challenge")
.put("params", new JSONObject().put("username", USERNAME))
.put("id", 0);
JSONObject challengeResponse = sendRequest(URL, challengeRequest.toString());
JSONObject challengeResult = challengeResponse.getJSONObject("result");
int alg = challengeResult.getInt("alg");
String salt = challengeResult.getString("salt");
String nonce = challengeResult.getString("nonce");
// Step2: Generate cipher text using openssl algorithm
String cipherPassword = Crypt.crypt(PASSWORD, "$" + alg + "$" + salt);
// Step3: Generate hash values for login
String hash = DigestUtils.md5Hex(USERNAME + ":" + cipherPassword + ":" + nonce);
// Step4: Get sid by login
JSONObject loginRequest = new JSONObject()
.put("jsonrpc", "2.0")
.put("method", "login")
.put("params", new JSONObject()
.put("username", USERNAME)
.put("hash", hash))
.put("id", 0);
JSONObject loginResponse = sendRequest(URL, loginRequest.toString());
String sid = loginResponse.getJSONObject("result").getString("sid");
// Step5: Calling other APIs with sid
JSONObject systemStatusRequest = new JSONObject()
.put("jsonrpc", "2.0")
.put("method", "call")
.put("params", new JSONArray()
.put(sid)
.put("system")
.put("get_status"))
.put("id", 0);
JSONObject systemStatusResponse = sendRequest(URL, systemStatusRequest.toString());
System.out.println(systemStatusResponse.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
private static JSONObject sendRequest(String url, String request) throws IOException {
HttpURLConnection connection = null;
try {
URL apiUrl = new URL(url);
connection = (HttpURLConnection) apiUrl.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoOutput(true);
OutputStream outputStream = connection.getOutputStream();
outputStream.write(request.getBytes());
outputStream.flush();
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return new JSONObject(response.toString());
} else {
throw new IOException("Request Exception: " + responseCode);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}
Dart
YAML
Copy
dependencies:
dio: any
crypto: any
gl_crypt:
git:
url: https://github.com/gl-inet/gl_crypt_dart.git
Dart
Copy
final String url = 'http://192.168.8.1/rpc';
final String username = 'root';
final String password = 'goodlife';
// Step1: Get encryption parameters by challenge method
Dio().post(
url,
data: {
'jsonrpc': '2.0',
'method': 'challenge',
'params': {
'username': username,
}
},
).then(
(value) {
Map result = jsonDecode(value.data)['result'];
int alg = result['alg'];
String salt = result['salt'];
String nonce = result['nonce'];
// Step2: Generate cipher text using Crypt
Crypt? pw;
switch (alg) {
case 1:
pw = Crypt.md5(password, salt: salt);
break;
case 5:
pw = Crypt.sha256(password, salt: salt);
break;
case 6:
pw = Crypt.sha512(password, salt: salt);
break;
default:
}
// Step3: Generate hash values for login
String hash =
md5.convert(utf8.encode('$username:$pw:$nonce')).toString();
// Step4: Get sid by login
Dio().post(
url,
data: {
'jsonrpc': '2.0',
'method': 'login',
'params': {
'username': username,
'hash': hash,
}
},
).then(
(value) {
Map result = jsonDecode(value.data)['result'];
String sid = result['sid'];
// Step5: Calling other APIs with sid
Dio().post(
url,
data: {
'jsonrpc': '2.0',
'method': 'call',
'params': [
sid,
'system',
'get_status',
]
},
).then(
(value) {
Map result = jsonDecode(value.data)['result'];
setState(() {
status = result;
});
print(result);
},
);
},
);
},
);
C/C++
C
Copy
#include <openssl/ssl.h>
#include <curl/curl.h>
#include <jansson.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <crypt.h>
struct curl_resp_data {
size_t size;
char *data;
};
static size_t curl_write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
struct curl_resp_data *resp = userp;
size_t realsize = size * nmemb;
char *data = realloc(resp->data, resp->size + realsize + 1);
if (!data)
return 0;
memcpy(data + resp->size, buffer, realsize);
resp->size += realsize;
resp->data = data;
data[resp->size] = '\0';
return realsize;
}
static json_t *call_challenge(CURL *curl, const char *username)
{
struct curl_resp_data resp = {};
json_t *data, *result = NULL;
char *body;
data = json_pack("{s:s,s:o}", "method", "challenge",
"params", json_pack("{s:s}", "username", username));
body = json_dumps(data, 0);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_perform(curl);
free(body);
data = json_loads(resp.data, 0, NULL);
free(resp.data);
json_unpack(data, "{s:O}", "result", &result);
json_decref(data);
return result;
}
static json_t *call_login(CURL *curl, const char *username, const char *hash)
{
struct curl_resp_data resp = {};
json_t *data, *result = NULL;
char *body;
data = json_pack("{s:s,s:o}", "method", "login",
"params", json_pack("{s:s,s:s}", "username", username, "hash", hash));
body = json_dumps(data, 0);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_perform(curl);
free(body);
data = json_loads(resp.data, 0, NULL);
free(resp.data);
json_unpack(data, "{s:O}", "result", &result);
json_decref(data);
return result;
}
int main(int argc, char const *argv[])
{
const char *url = "http://192.168.8.1/rpc";
const char *username = "root";
const char *password = "1";
const char *salt, *nonce, *pw, *sid;
uint8_t md5_result[16];
int alg;
char setting[256], hash[33] = "";
#if OPENSSL_VERSION_MAJOR < 3
MD5_CTX ctx;
#else
EVP_MD_CTX *ctx;
unsigned int md5len;
#endif
json_t *result;
int i;
CURL *curl;
curl = curl_easy_init();
if (!curl) {
fprintf(stderr, "curl_easy_init fail\n");
return -1;
}
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
result = call_challenge(curl, username);
if (!result) {
fprintf(stderr, "invalid username\n");
curl_easy_cleanup(curl);
return -1;
}
json_unpack(result, "{s:i,s:s,s:s}", "alg", &alg, "salt", &salt, "nonce", &nonce);
sprintf(setting, "$%d$%s$", alg, salt);
pw = crypt(password, setting);
#if OPENSSL_VERSION_MAJOR < 3
MD5_Init(&ctx);
MD5_Update(&ctx, username, strlen(username));
MD5_Update(&ctx, ":", 1);
MD5_Update(&ctx, pw, strlen(pw));
MD5_Update(&ctx, ":", 1);
MD5_Update(&ctx, nonce, strlen(nonce));
MD5_Final(md5_result, &ctx);
#else
ctx = EVP_MD_CTX_new();
EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
EVP_DigestUpdate(ctx, username, strlen(username));
EVP_DigestUpdate(ctx, ":", 1);
EVP_DigestUpdate(ctx, pw, strlen(pw));
EVP_DigestUpdate(ctx, ":", 1);
EVP_DigestUpdate(ctx, nonce, strlen(nonce));
EVP_DigestFinal(ctx, md5_result, &md5len);
EVP_MD_CTX_free(ctx);
#endif
json_decref(result);
for (i = 0; i < 16; i++)
sprintf(&hash[i * 2], "%02x", md5_result[i]);
result = call_login(curl, username, hash);
if (!result) {
fprintf(stderr, "invalid password\n");
curl_easy_cleanup(curl);
return -1;
}
json_unpack(result, "{s:s}", "sid", &sid);
printf("sid: %s\n", sid);
json_decref(result);
curl_easy_cleanup(curl);
return 0;
}
Bash
Copy
sudo apt install libssl-dev libcurl4-openssl-dev libjansson-dev
gcc test.c -lcurl -ljansson -lcrypt -lcrypto
Compile Command