-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathubus.sh
154 lines (124 loc) · 4.12 KB
/
ubus.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/bin/sh
[ "$REQUEST_METHOD" != "POST" ] && printf 'Status: 405\r\n\r\n' && exit
access() {
local sid=$1
local obj=$2
local fun=$3
local req=$(printf '{ "ubus_rpc_session": "%s", "scope": "ubus", "object": "%s", "function": "%s" }' "$sid" "$obj" "$fun")
local res=$(ubus call session access "$req" | jsonfilter -e '@.access')
[ "$res" = "true" ]
}
error() {
local code=$1
local mesg=$2
printf '{ "jsonrpc": "2.0", "id": "%s", "error": { "code": %d, "message": "%s" } }' \
"${RPC_ID:-null}" "$code" "$mesg"
exit 1
}
process() {
local request=$1
# - use `VAR=expr` notation to let it create shell compatible export statements
# - eval result to import variables
eval $(jsonfilter -s "$request" \
-e '[email protected]' \
-e '[email protected]' \
-e '[email protected]' \
-e '[email protected][3].ubus_rpc_session' \
-e '[email protected][0]' \
-e '[email protected][1]' \
-e '[email protected][2]')
# verify JSON-RPC framing
if [ -z "$RPC_ID" ] || [ "$RPC_VERSION" != "2.0" ]; then
error -32600 "Invalid request"
fi
# reject invalid values to prevent shell injection
case "$RPC_ID$UBUS_SID$UBUS_SERVICE$UBUS_CMD" in
*[^a-zA-Z0-9_.-]*) error -32600 "Invalid request" ;;
esac
case "$RPC_METHOD" in
call)
UBUS_PAYLOAD=$(jsonfilter -s "$request" -e '@.params[3]')
# ensure that payload is a dictionary or empty
case "$UBUS_PAYLOAD" in
""|{*}) : ;;
*) error -32602 "Invalid parameters" ;;
esac
# merge ubus_rpc_session parameter
if [ -z "$UBUS_PAYLOAD" ] || [ "$UBUS_PAYLOAD" = "{ }" ]; then
UBUS_PAYLOAD=$(printf '{ "ubus_rpc_session": "%s" }' "$UBUS_SID")
else
UBUS_PAYLOAD=$(printf '{ "ubus_rpc_session": "%s", %s' "$UBUS_SID" "${UBUS_PAYLOAD#\{ }")
fi
# reject requests with embedded ubus_rpc_session
if [ -n "$RPC_SESSION_ARG" ]; then
error -32602 "Invalid parameters"
fi
# check access
if ! access "$UBUS_SID" "$UBUS_SERVICE" "$UBUS_CMD"; then
error -32002 "Access denied"
fi
ubus_reply=$(ubus call "$UBUS_SERVICE" "$UBUS_CMD" "$UBUS_PAYLOAD")
ubus_status=$?
printf '{ "jsonrpc": "2.0", "id": "%s", "result": [ %d, %s ] }' \
"$RPC_ID" "$ubus_status" "${ubus_reply:-null}"
;;
list)
RPC_PARAMS=$(jsonfilter -s "$request" -e '@.params')
# ensure that payload is an array or empty
case "${RPC_PARAMS:-[ ]}" in
\[*\]) : ;;
*) error -32602 "Invalid parameters" ;;
esac
# empty payload should result in list of services
if [ "${RPC_PARAMS:-[ ]}" = "[ ]" ]; then
services=''
for service in $(ubus list); do
services="${services:+$services, }\"$service\""
done
printf '{ "jsonrpc": "2.0", "id": "%s", "result": [ %s ] }' \
"$RPC_ID" "$services"
# list of services should result in { service => { method => signature } } replies
else
signatures=''
eval $(jsonfilter -s "$RPC_PARAMS" -e 'indexes=@')
for i in $indexes; do
service=$(jsonfilter -s "$RPC_PARAMS" -e "@[$i]")
signature=''
IFS=$'\n\t'
for line in $(ubus -v list "$service" | tail -n +2); do
signature="${signature:+$signature, }$line"
done
IFS=$' \n\t'
signatures="${signatures:+$signatures, }\"$service\": { $signature }"
done
printf '{ "jsonrpc": "2.0", "id": "%s", "result": { %s } }' \
"$RPC_ID" "$signatures"
fi
;;
*)
error -32601 "Method not found"
;;
esac
}
# - read body from stdin (either an object or array)
# - process each item if it is an array
body=$(cat)
type=$(jsonfilter -s "$body" -t '@')
printf 'Content-Type: application/json\r\n\r\n'
if [ "$type" = "array" ]; then
first=true
printf '['
jsonfilter -s "$body" -e '@.*' | while read request ; do
# join response with ',' and the first should be omitted
if ! $first; then
printf ','
else
first=false
fi
# process each request
process "$request"
done
printf ']'
else
process "$body"
fi