Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
48ace3de
Commit
48ace3de
authored
May 29, 2017
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Big refactor: Add request cache and collector
parent
3395c845
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
307 additions
and
205 deletions
+307
-205
activity.c
activity.c
+2
-2
db.c
db.c
+21
-32
su.c
su.c
+64
-138
su.h
su.h
+19
-9
su_daemon.c
su_daemon.c
+196
-20
su_socket.c
su_socket.c
+5
-4
No files found.
activity.c
View file @
48ace3de
...
...
@@ -54,13 +54,13 @@ static int setup_user(struct su_context *ctx, char* user) {
void
app_send_result
(
struct
su_context
*
ctx
,
policy_t
policy
)
{
char
fromUid
[
16
];
sprintf
(
fromUid
,
"%d"
,
ctx
->
from
.
uid
);
sprintf
(
fromUid
,
"%d"
,
ctx
->
info
->
uid
);
char
toUid
[
16
];
sprintf
(
toUid
,
"%d"
,
ctx
->
to
.
uid
);
char
pid
[
16
];
sprintf
(
pid
,
"%d"
,
ctx
->
from
.
pid
);
sprintf
(
pid
,
"%d"
,
ctx
->
info
->
pid
);
char
user
[
16
];
int
notify
=
setup_user
(
ctx
,
user
);
...
...
db.c
View file @
48ace3de
...
...
@@ -9,67 +9,56 @@
#include <sqlite3.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include "magisk.h"
#include "su.h"
struct
callback_data_t
{
struct
su_context
*
ctx
;
policy_t
policy
;
};
static
int
database_callback
(
void
*
v
,
int
argc
,
char
**
argv
,
char
**
azColName
){
struct
callback_data_t
*
data
=
(
struct
callback_data_t
*
)
v
;
policy_t
policy
=
INTERACTIVE
;
int
i
;
static
int
database_callback
(
void
*
v
,
int
argc
,
char
**
argv
,
char
**
azColName
)
{
struct
su_context
*
ctx
=
(
struct
su_context
*
)
v
;
policy_t
policy
=
QUERY
;
time_t
until
=
0
;
for
(
i
=
0
;
i
<
argc
;
i
++
)
{
for
(
i
nt
i
=
0
;
i
<
argc
;
i
++
)
{
if
(
strcmp
(
azColName
[
i
],
"policy"
)
==
0
)
{
if
(
argv
[
i
]
!=
NULL
)
{
policy
=
atoi
(
argv
[
i
]);
}
}
else
if
(
strcmp
(
azColName
[
i
],
"until"
)
==
0
)
{
}
else
if
(
strcmp
(
azColName
[
i
],
"until"
)
==
0
)
{
if
(
argv
[
i
]
!=
NULL
)
{
until
=
atol
(
argv
[
i
]);
}
}
}
if
(
policy
==
DENY
)
{
data
->
policy
=
DENY
;
return
-
1
;
}
else
if
(
policy
==
ALLOW
&&
(
until
==
0
||
until
>
time
(
NULL
)))
{
data
->
policy
=
ALLOW
;
// even though we allow, continue, so we can see if there's another policy
// that denies...
}
if
(
policy
==
DENY
)
ctx
->
info
->
policy
=
DENY
;
else
if
(
policy
==
ALLOW
&&
(
until
==
0
||
until
>
time
(
NULL
)))
ctx
->
info
->
policy
=
ALLOW
;
return
0
;
}
policy_t
database_check
(
struct
su_context
*
ctx
)
{
void
database_check
(
struct
su_context
*
ctx
)
{
sqlite3
*
db
=
NULL
;
// Check if file is readable
if
(
access
(
ctx
->
user
.
database_path
,
R_OK
)
==
-
1
)
return
;
char
query
[
512
];
snprintf
(
query
,
sizeof
(
query
),
"
select policy, until from policies where uid=%d"
,
ctx
->
from
.
uid
);
snprintf
(
query
,
sizeof
(
query
),
"
SELECT policy, until FROM policies WHERE uid=%d"
,
ctx
->
info
->
uid
%
100000
);
int
ret
=
sqlite3_open_v2
(
ctx
->
user
.
database_path
,
&
db
,
SQLITE_OPEN_READONLY
,
NULL
);
if
(
ret
)
{
LOG
E
(
"sqlite3 open failure: %d"
,
ret
);
LOG
D
(
"sqlite3 open failure: %s
\n
"
,
sqlite3_errstr
(
ret
)
);
sqlite3_close
(
db
);
return
INTERACTIVE
;
return
;
}
char
*
err
=
NULL
;
struct
callback_data_t
data
;
data
.
ctx
=
ctx
;
data
.
policy
=
INTERACTIVE
;
ret
=
sqlite3_exec
(
db
,
query
,
database_callback
,
&
data
,
&
err
);
ret
=
sqlite3_exec
(
db
,
query
,
database_callback
,
ctx
,
&
err
);
sqlite3_close
(
db
);
if
(
err
!=
NULL
)
{
LOGE
(
"sqlite3_exec: %s"
,
err
);
return
DENY
;
LOGE
(
"sqlite3_exec: %s
\n
"
,
err
);
ctx
->
info
->
policy
=
DENY
;
}
return
data
.
policy
;
}
su.c
View file @
48ace3de
...
...
@@ -27,7 +27,7 @@
#include "resetprop.h"
#include "su.h"
st
atic
st
ruct
su_context
*
su_ctx
;
struct
su_context
*
su_ctx
;
static
void
usage
(
int
status
)
{
FILE
*
stream
=
(
status
==
EXIT_SUCCESS
)
?
stdout
:
stderr
;
...
...
@@ -46,7 +46,7 @@ static void usage(int status) {
" -v, --version display version number and exit
\n
"
" -V display version code and exit,
\n
"
" this is used almost exclusively by Superuser.apk
\n
"
);
exit
(
status
);
exit
2
(
status
);
}
static
char
*
concat_commands
(
int
argc
,
char
*
argv
[])
{
...
...
@@ -61,16 +61,6 @@ static char *concat_commands(int argc, char *argv[]) {
return
strdup
(
command
);
}
static
int
get_multiuser_mode
()
{
char
*
prop
=
getprop
(
MULTIUSER_MODE_PROP
);
if
(
prop
)
{
int
ret
=
atoi
(
prop
);
free
(
prop
);
return
ret
;
}
return
MULTIUSER_MODE_OWNER_ONLY
;
}
static
void
populate_environment
(
const
struct
su_context
*
ctx
)
{
struct
passwd
*
pw
;
...
...
@@ -96,8 +86,7 @@ static __attribute__ ((noreturn)) void allow() {
umask
(
su_ctx
->
umask
);
// no need to log if called by root
if
(
su_ctx
->
from
.
uid
!=
UID_ROOT
)
if
(
su_ctx
->
notify
)
app_send_result
(
su_ctx
,
ALLOW
);
char
*
binary
=
su_ctx
->
to
.
shell
;
...
...
@@ -129,11 +118,10 @@ static __attribute__ ((noreturn)) void allow() {
}
static
__attribute__
((
noreturn
))
void
deny
()
{
// no need to log if called by root
if
(
su_ctx
->
from
.
uid
!=
UID_ROOT
)
if
(
su_ctx
->
notify
)
app_send_result
(
su_ctx
,
DENY
);
LOGW
(
"su: request rejected (%u->%u)"
,
su_ctx
->
from
.
uid
,
su_ctx
->
to
.
uid
);
LOGW
(
"su: request rejected (%u->%u)"
,
su_ctx
->
info
->
uid
,
su_ctx
->
to
.
uid
);
fprintf
(
stderr
,
"%s
\n
"
,
strerror
(
EACCES
));
exit
(
EXIT_FAILURE
);
}
...
...
@@ -147,7 +135,17 @@ static void socket_cleanup() {
static
void
cleanup_signal
(
int
sig
)
{
socket_cleanup
();
exit
(
EXIT_FAILURE
);
exit2
(
EXIT_FAILURE
);
}
__attribute__
((
noreturn
))
void
exit2
(
int
status
)
{
// Handle the pipe, or the daemon will get stuck
if
(
su_ctx
->
info
->
policy
==
QUERY
)
{
xwrite
(
pipefd
[
1
],
&
su_ctx
->
info
->
policy
,
sizeof
(
su_ctx
->
info
->
policy
));
close
(
pipefd
[
0
]);
close
(
pipefd
[
1
]);
}
exit
(
status
);
}
int
su_daemon_main
(
int
argc
,
char
**
argv
)
{
...
...
@@ -199,35 +197,8 @@ int su_daemon_main(int argc, char **argv) {
}
}
// Default values
struct
su_context
ctx
=
{
.
from
=
{
.
pid
=
su_credentials
.
pid
,
.
uid
=
su_credentials
.
uid
,
},
.
to
=
{
.
uid
=
UID_ROOT
,
.
login
=
0
,
.
keepenv
=
0
,
.
shell
=
DEFAULT_SHELL
,
.
command
=
NULL
,
.
argv
=
argv
,
.
argc
=
argc
,
},
.
user
=
{
.
android_user_id
=
0
,
.
multiuser_mode
=
get_multiuser_mode
(),
.
database_path
=
APP_DATA_PATH
REQUESTOR_DATABASE_PATH
,
.
base_path
=
APP_DATA_PATH
REQUESTOR
},
.
umask
=
022
,
};
su_ctx
=
&
ctx
;
struct
stat
st
;
int
c
,
socket_serv_fd
,
fd
;
char
result
[
64
];
policy_t
dballow
;
struct
option
long_opts
[]
=
{
{
"command"
,
required_argument
,
NULL
,
'c'
},
{
"help"
,
no_argument
,
NULL
,
'h'
},
...
...
@@ -242,30 +213,31 @@ int su_daemon_main(int argc, char **argv) {
while
((
c
=
getopt_long
(
argc
,
argv
,
"c:hlmps:Vvuz:"
,
long_opts
,
NULL
))
!=
-
1
)
{
switch
(
c
)
{
case
'c'
:
ctx
.
to
.
command
=
concat_commands
(
argc
,
argv
);
su_ctx
->
to
.
command
=
concat_commands
(
argc
,
argv
);
optind
=
argc
;
su_ctx
->
notify
=
1
;
break
;
case
'h'
:
usage
(
EXIT_SUCCESS
);
break
;
case
'l'
:
ctx
.
to
.
login
=
1
;
su_ctx
->
to
.
login
=
1
;
break
;
case
'm'
:
case
'p'
:
ctx
.
to
.
keepenv
=
1
;
su_ctx
->
to
.
keepenv
=
1
;
break
;
case
's'
:
ctx
.
to
.
shell
=
optarg
;
su_ctx
->
to
.
shell
=
optarg
;
break
;
case
'V'
:
printf
(
"%d
\n
"
,
MAGISK_VER_CODE
);
exit
(
EXIT_SUCCESS
);
exit
2
(
EXIT_SUCCESS
);
case
'v'
:
printf
(
"%s
\n
"
,
MAGISKSU_VER_STR
);
exit
(
EXIT_SUCCESS
);
exit
2
(
EXIT_SUCCESS
);
case
'u'
:
switch
(
ctx
.
user
.
multiuser_mode
)
{
switch
(
su_ctx
->
user
.
multiuser_mode
)
{
case
MULTIUSER_MODE_USER
:
printf
(
"Owner only: Only owner has root access
\n
"
);
break
;
...
...
@@ -276,7 +248,7 @@ int su_daemon_main(int argc, char **argv) {
printf
(
"User independent: Each user has its own separate root rules
\n
"
);
break
;
}
exit
(
EXIT_SUCCESS
);
exit
2
(
EXIT_SUCCESS
);
case
'z'
:
// Do nothing, placed here for legacy support :)
break
;
...
...
@@ -286,8 +258,12 @@ int su_daemon_main(int argc, char **argv) {
usage
(
2
);
}
}
su_ctx
->
to
.
argc
=
argc
;
su_ctx
->
to
.
argv
=
argv
;
if
(
optind
<
argc
&&
!
strcmp
(
argv
[
optind
],
"-"
))
{
ctx
.
to
.
login
=
1
;
su_ctx
->
to
.
login
=
1
;
optind
++
;
}
/* username or uid */
...
...
@@ -299,14 +275,14 @@ int su_daemon_main(int argc, char **argv) {
/* It seems we shouldn't do this at all */
errno
=
0
;
ctx
.
to
.
uid
=
strtoul
(
argv
[
optind
],
&
endptr
,
10
);
su_ctx
->
to
.
uid
=
strtoul
(
argv
[
optind
],
&
endptr
,
10
);
if
(
errno
||
*
endptr
)
{
LOGE
(
"Unknown id: %s
\n
"
,
argv
[
optind
]);
fprintf
(
stderr
,
"Unknown id: %s
\n
"
,
argv
[
optind
]);
exit
(
EXIT_FAILURE
);
}
}
else
{
ctx
.
to
.
uid
=
pw
->
pw_uid
;
su_ctx
->
to
.
uid
=
pw
->
pw_uid
;
}
optind
++
;
}
...
...
@@ -314,54 +290,28 @@ int su_daemon_main(int argc, char **argv) {
optind
++
;
}
//
The su_context setup is
done, now every error leads to deny
//
Setup
done, now every error leads to deny
err_handler
=
deny
;
// It's in multiuser mode
if
(
ctx
.
from
.
uid
>
99999
)
{
ctx
.
user
.
android_user_id
=
ctx
.
from
.
uid
/
100000
;
ctx
.
from
.
uid
%=
100000
;
switch
(
ctx
.
user
.
multiuser_mode
)
{
case
MULTIUSER_MODE_OWNER_ONLY
:
deny
();
case
MULTIUSER_MODE_USER
:
snprintf
(
ctx
.
user
.
database_path
,
PATH_MAX
,
"%s/%d/%s"
,
USER_DATA_PATH
,
ctx
.
user
.
android_user_id
,
REQUESTOR_DATABASE_PATH
);
snprintf
(
ctx
.
user
.
base_path
,
PATH_MAX
,
"%s/%d/%s"
,
USER_DATA_PATH
,
ctx
.
user
.
android_user_id
,
REQUESTOR
);
break
;
}
}
// verify if Magisk Manager is installed
xstat
(
ctx
.
user
.
base_path
,
&
st
);
// always allow if this is Magisk Manager
if
(
ctx
.
from
.
uid
==
(
st
.
st_uid
%
100000
))
{
allow
();
}
// odd perms on superuser data dir
if
(
st
.
st_gid
!=
st
.
st_uid
)
{
LOGE
(
"Bad uid/gid %d/%d for Superuser Requestor application"
,
(
int
)
st
.
st_uid
,
(
int
)
st
.
st_gid
);
deny
();
}
// Check property of root configuration
char
*
root_prop
=
getprop
(
ROOT_ACCESS_PROP
);
if
(
root_prop
)
{
int
prop_status
=
atoi
(
root_prop
);
switch
(
prop_status
)
{
case
ROOT_ACCESS_DISABLED
:
LOGE
(
"Root access is disabled!
\n
"
);
exit
(
EXIT_FAILURE
);
case
ROOT_ACCESS_APPS_ONLY
:
if
(
ctx
.
from
.
uid
==
UID_SHELL
)
if
(
su_ctx
->
info
->
uid
==
UID_SHELL
)
{
LOGE
(
"Root access is disabled for ADB!
\n
"
);
exit
(
EXIT_FAILURE
);
}
break
;
case
ROOT_ACCESS_ADB_ONLY
:
if
(
ctx
.
from
.
uid
!=
UID_SHELL
)
if
(
su_ctx
->
info
->
uid
!=
UID_SHELL
)
{
LOGE
(
"Root access limited to ADB only!
\n
"
);
exit
(
EXIT_FAILURE
);
}
break
;
case
ROOT_ACCESS_APPS_AND_ADB
:
default
:
...
...
@@ -373,62 +323,38 @@ int su_daemon_main(int argc, char **argv) {
setprop
(
ROOT_ACCESS_PROP
,
xstr
(
ROOT_ACCESS_APPS_AND_ADB
));
}
//
Allow root to start root
if
(
ctx
.
from
.
uid
==
UID_ROOT
)
{
allow
(
);
}
//
New request or no db exist, notify user for response
if
(
su_ctx
->
info
->
policy
==
QUERY
)
{
socket_serv_fd
=
socket_create_temp
(
su_ctx
->
sock_path
,
sizeof
(
su_ctx
->
sock_path
)
);
setup_sighandlers
(
cleanup_signal
);
mkdir
(
REQUESTOR_CACHE_PATH
,
0770
);
if
(
chown
(
REQUESTOR_CACHE_PATH
,
st
.
st_uid
,
st
.
st_gid
))
{
PLOGE
(
"chown (%s, %u, %u)"
,
REQUESTOR_CACHE_PATH
,
st
.
st_uid
,
st
.
st_gid
);
}
// Start activity
app_send_request
(
su_ctx
);
if
(
setgroups
(
0
,
NULL
))
{
PLOGE
(
"setgroups"
);
}
if
(
setegid
(
st
.
st_gid
))
{
PLOGE
(
"setegid (%u)"
,
st
.
st_gid
);
}
if
(
seteuid
(
st
.
st_uid
))
{
PLOGE
(
"seteuid (%u)"
,
st
.
st_uid
);
}
atexit
(
socket_cleanup
);
// If db exits, check directly instead query application
dballow
=
database_check
(
&
ctx
);
switch
(
dballow
)
{
case
INTERACTIVE
:
break
;
case
ALLOW
:
allow
();
case
DENY
:
default:
deny
();
}
// New request or no db exist, notify app for response
socket_serv_fd
=
socket_create_temp
(
ctx
.
sock_path
,
sizeof
(
ctx
.
sock_path
));
setup_sighandlers
(
cleanup_signal
);
fd
=
socket_accept
(
socket_serv_fd
);
socket_send_request
(
fd
,
su_ctx
);
socket_receive_result
(
fd
,
result
,
sizeof
(
result
));
// Start activity
app_send_request
(
su_ctx
);
close
(
fd
);
close
(
socket_serv_fd
);
socket_cleanup
();
atexit
(
socket_cleanup
);
fd
=
socket_accept
(
socket_serv_fd
);
socket_send_request
(
fd
,
&
ctx
);
socket_receive_result
(
fd
,
result
,
sizeof
(
result
));
if
(
strcmp
(
result
,
"socket:ALLOW"
)
==
0
)
su_ctx
->
info
->
policy
=
ALLOW
;
else
su_ctx
->
info
->
policy
=
DENY
;
close
(
fd
);
close
(
socket_serv_fd
);
socket_cleanup
();
// Report the policy to main daemon
xwrite
(
pipefd
[
1
],
&
su_ctx
->
info
->
policy
,
sizeof
(
su_ctx
->
info
->
policy
));
close
(
pipefd
[
0
]);
close
(
pipefd
[
1
]);
}
if
(
strcmp
(
result
,
"socket:DENY"
)
==
0
)
{
deny
();
}
else
if
(
strcmp
(
result
,
"socket:ALLOW"
)
==
0
)
{
if
(
su_ctx
->
info
->
policy
==
ALLOW
)
allow
();
}
else
{
LOGE
(
"unknown response from Superuser Requestor: %s"
,
result
);
else
deny
();
}
}
su.h
View file @
48ace3de
...
...
@@ -7,6 +7,8 @@
#include <limits.h>
#include <sys/types.h>
#include "list.h"
#define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)"
// Property check for root access
...
...
@@ -43,9 +45,20 @@
#define DEFAULT_SHELL "/system/bin/sh"
typedef
enum
{
QUERY
=
0
,
DENY
=
1
,
ALLOW
=
2
,
}
policy_t
;
struct
su_initiator
{
pid_t
pid
;
unsigned
uid
;
policy_t
policy
;
pthread_mutex_t
lock
;
int
count
;
int
clock
;
struct
list_head
pos
;
};
struct
su_request
{
...
...
@@ -76,24 +89,21 @@ struct su_user_info {
};
struct
su_context
{
struct
su_initiator
from
;
struct
su_initiator
*
info
;
struct
su_request
to
;
struct
su_user_info
user
;
int
notify
;
mode_t
umask
;
char
sock_path
[
PATH_MAX
];
};
typedef
enum
{
INTERACTIVE
=
0
,
DENY
=
1
,
ALLOW
=
2
,
}
policy_t
;
extern
struct
ucred
su_credentials
;
extern
struct
su_context
*
su_ctx
;
extern
int
pipefd
[
2
];
// su.c
int
su_daemon_main
(
int
argc
,
char
**
argv
);
__attribute__
((
noreturn
))
void
exit2
(
int
status
);
// su_client.c
...
...
@@ -109,7 +119,7 @@ void app_send_request(struct su_context *ctx);
// db.c
policy_t
database_check
(
struct
su_context
*
ctx
);
void
database_check
(
struct
su_context
*
ctx
);
// misc.c
...
...
su_
client
.c
→
su_
daemon
.c
View file @
48ace3de
/* su_client.c - The entrypoint for su, connect to daemon and send correct info
*/
#define _GNU_SOURCE
#include <limits.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
...
...
@@ -16,17 +17,24 @@
#include "magisk.h"
#include "daemon.h"
#include "resetprop.h"
#include "utils.h"
#include "su.h"
#include "pts.h"
#include "list.h"
// Constants for the atty bitfield
#define ATTY_IN 1
#define ATTY_OUT 2
#define ATTY_ERR 4
struct
ucred
su_credentials
;
static
__thread
int
client_fd
;
#define TIMEOUT 3
static
struct
list_head
active_list
,
waiting_list
;
static
pthread_t
su_collector
=
0
;
static
pthread_mutex_t
list_lock
=
PTHREAD_MUTEX_INITIALIZER
;
int
pipefd
[
2
];
static
void
sighandler
(
int
sig
)
{
restore_stdin
();
...
...
@@ -53,27 +61,181 @@ static void sighandler(int sig) {
static
void
sigpipe_handler
(
int
sig
)
{
LOGD
(
"su: Client killed unexpectedly
\n
"
);
close
(
client_fd
);
pthread_exit
(
NULL
);
}
static
int
get_multiuser_mode
()
{
char
*
prop
=
getprop
(
MULTIUSER_MODE_PROP
);
if
(
prop
)
{
int
ret
=
atoi
(
prop
);
free
(
prop
);
return
ret
;
}
return
MULTIUSER_MODE_OWNER_ONLY
;
}
// Maintain the lists periodically
static
void
*
collector
(
void
*
args
)
{
LOGD
(
"su: collector started
\n
"
);
struct
list_head
*
pos
,
*
temp
;
struct
su_initiator
*
node
;
while
(
1
)
{
sleep
(
1
);
pthread_mutex_lock
(
&
list_lock
);
list_for_each
(
pos
,
&
active_list
)
{
node
=
list_entry
(
pos
,
struct
su_initiator
,
pos
);
--
node
->
clock
;
// Timeout, move to waiting list
if
(
node
->
clock
==
0
)
{
temp
=
pos
;
pos
=
pos
->
prev
;
list_pop
(
temp
);
list_insert_end
(
&
waiting_list
,
temp
);
}
}
list_for_each
(
pos
,
&
waiting_list
)
{
node
=
list_entry
(
pos
,
struct
su_initiator
,
pos
);
// Nothing is using the info, remove it
if
(
node
->
count
==
0
)
{
temp
=
pos
;
pos
=
pos
->
prev
;
list_pop
(
temp
);
pthread_mutex_destroy
(
&
node
->
lock
);
free
(
node
);
}
}
pthread_mutex_unlock
(
&
list_lock
);
}
}
void
su_daemon_receiver
(
int
client
)
{
LOGD
(
"su: get request from client: %d
\n
"
,
client
);
LOGD
(
"su: request from client: %d
\n
"
,
client
);
struct
su_initiator
*
info
=
NULL
,
*
node
;
struct
list_head
*
pos
;
int
new_request
=
0
;
pthread_mutex_lock
(
&
list_lock
);
if
(
!
su_collector
)
{
init_list_head
(
&
active_list
);
init_list_head
(
&
waiting_list
);
xpthread_create
(
&
su_collector
,
NULL
,
collector
,
NULL
);
}
// Get client credntial
struct
ucred
credential
;
get_client_cred
(
client
,
&
credential
);
// Search for existing in the active list
list_for_each
(
pos
,
&
active_list
)
{
node
=
list_entry
(
pos
,
struct
su_initiator
,
pos
);
if
(
node
->
uid
==
credential
.
uid
)
info
=
node
;
}
// If no exist, create a new request
if
(
info
==
NULL
)
{
new_request
=
1
;
info
=
malloc
(
sizeof
(
*
info
));
info
->
uid
=
credential
.
uid
;
info
->
policy
=
QUERY
;
info
->
count
=
0
;
pthread_mutex_init
(
&
info
->
lock
,
NULL
);
list_insert_end
(
&
active_list
,
&
info
->
pos
);
}
info
->
clock
=
TIMEOUT
;
/* Reset timer */
++
info
->
count
;
/* Increment reference count */
pthread_mutex_unlock
(
&
list_lock
);
LOGD
(
"su: request from uid=[%d] (#%d)
\n
"
,
info
->
uid
,
info
->
count
);
// Lock before the policy is determined
pthread_mutex_lock
(
&
info
->
lock
);
info
->
pid
=
credential
.
pid
;
// Default values
struct
su_context
ctx
=
{
.
info
=
info
,
.
to
=
{
.
uid
=
UID_ROOT
,
.
login
=
0
,
.
keepenv
=
0
,
.
shell
=
DEFAULT_SHELL
,
.
command
=
NULL
,
},
.
user
=
{
.
android_user_id
=
info
->
uid
/
100000
,
.
multiuser_mode
=
get_multiuser_mode
(),
},
.
umask
=
022
,
.
notify
=
new_request
,
};
su_ctx
=
&
ctx
;
snprintf
(
su_ctx
->
user
.
database_path
,
PATH_MAX
,
"%s/%d/%s"
,
USER_DATA_PATH
,
su_ctx
->
user
.
android_user_id
,
REQUESTOR_DATABASE_PATH
);
snprintf
(
su_ctx
->
user
.
base_path
,
PATH_MAX
,
"%s/%d/%s"
,
USER_DATA_PATH
,
su_ctx
->
user
.
android_user_id
,
REQUESTOR
);
// verify if Magisk Manager is installed
struct
stat
st
;
xstat
(
su_ctx
->
user
.
base_path
,
&
st
);
// odd perms on superuser data dir
if
(
st
.
st_gid
!=
st
.
st_uid
)
{
LOGE
(
"Bad uid/gid %d/%d for Superuser Requestor application"
,
st
.
st_uid
,
st
.
st_gid
);
info
->
policy
=
DENY
;
}
// Not cached, do the checks
if
(
info
->
policy
==
QUERY
)
{
if
(
su_ctx
->
user
.
android_user_id
&&
su_ctx
->
user
.
multiuser_mode
==
MULTIUSER_MODE_OWNER_ONLY
)
{
info
->
policy
=
DENY
;
su_ctx
->
notify
=
0
;
}
// always allow if this is Magisk Manager
if
(
info
->
policy
==
QUERY
&&
(
info
->
uid
%
100000
)
==
(
st
.
st_uid
%
100000
))
{
info
->
policy
=
ALLOW
;
su_ctx
->
notify
=
0
;
}
// always allow if it's root
if
(
info
->
uid
==
UID_ROOT
)
{
info
->
policy
=
ALLOW
;
su_ctx
->
notify
=
0
;
}
// If not determined, check database
if
(
info
->
policy
==
QUERY
)
database_check
(
su_ctx
);
// If still not determined, open a pipe and wait for results
if
(
info
->
policy
==
QUERY
)
pipe2
(
pipefd
,
O_CLOEXEC
);
}
// Fork a new process, the child process will need to setsid,
// open a pseudo-terminal if needed, and will eventually run exec
// The parent process will wait for the result and
// send the return code back to our client
int
child
=
fork
();
if
(
child
<
0
)
{
write
(
client
,
&
child
,
sizeof
(
child
));
close
(
client
);
if
(
child
<
0
)
PLOGE
(
"fork"
);
return
;
}
else
if
(
child
!=
0
)
{
// For sighandler to close it
client_fd
=
client
;
if
(
child
)
{
// Wait for results
if
(
info
->
policy
==
QUERY
)
{
xxread
(
pipefd
[
0
],
&
info
->
policy
,
sizeof
(
info
->
policy
));
close
(
pipefd
[
0
]);
close
(
pipefd
[
1
]);
}
// The policy is determined, unlock
pthread_mutex_unlock
(
&
info
->
lock
);
// Wait result
LOGD
(
"su: wait_result waiting for %d
\n
"
,
child
);
...
...
@@ -91,10 +253,13 @@ void su_daemon_receiver(int client) {
code
=
-
1
;
// Pass the return code back to the client
write
_int
(
client
,
code
);
write
(
client
,
&
code
,
sizeof
(
code
));
/* Might SIGPIPE, ignored */
LOGD
(
"su: return code to client: %d
\n
"
,
code
);
close
(
client
);
// Decrement reference count
--
info
->
count
;
return
;
}
...
...
@@ -106,14 +271,11 @@ void su_daemon_receiver(int client) {
// Become session leader
xsetsid
();
// Get the credentials
get_client_cred
(
client
,
&
su_credentials
);
// Let's read some info from the socket
int
argc
=
read_int
(
client
);
if
(
argc
<
0
||
argc
>
512
)
{
LOGE
(
"unable to allocate args: %d"
,
argc
);
exit
(
1
);
exit
2
(
1
);
}
LOGD
(
"su: argc=[%d]
\n
"
,
argc
);
...
...
@@ -149,9 +311,9 @@ void su_daemon_receiver(int client) {
xstat
(
pts_slave
,
&
stbuf
);
//If caller is not root, ensure the owner of pts_slave is the caller
if
(
stbuf
.
st_uid
!=
su_credentials
.
uid
&&
su_credentials
.
uid
!=
0
)
{
if
(
stbuf
.
st_uid
!=
credential
.
uid
&&
credential
.
uid
!=
0
)
{
LOGE
(
"su: Wrong permission of pts_slave"
);
exit
(
1
);
exit
2
(
1
);
}
// Opening the TTY has to occur after the
...
...
@@ -182,6 +344,20 @@ void su_daemon_receiver(int client) {
close
(
ptsfd
);
mkdir
(
REQUESTOR_CACHE_PATH
,
0770
);
if
(
chown
(
REQUESTOR_CACHE_PATH
,
st
.
st_uid
,
st
.
st_gid
))
PLOGE
(
"chown (%s, %u, %u)"
,
REQUESTOR_CACHE_PATH
,
st
.
st_uid
,
st
.
st_gid
);
if
(
setgroups
(
0
,
NULL
))
PLOGE
(
"setgroups"
);
if
(
setegid
(
st
.
st_gid
))
PLOGE
(
"setegid (%u)"
,
st
.
st_gid
);
if
(
seteuid
(
st
.
st_uid
))
PLOGE
(
"seteuid (%u)"
,
st
.
st_uid
);
su_daemon_main
(
argc
,
argv
);
}
...
...
su_socket.c
View file @
48ace3de
...
...
@@ -12,6 +12,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <selinux/selinux.h>
#include <syscall.h>
#include "magisk.h"
#include "utils.h"
...
...
@@ -28,7 +29,7 @@ int socket_create_temp(char *path, size_t len) {
memset
(
&
sun
,
0
,
sizeof
(
sun
));
sun
.
sun_family
=
AF_LOCAL
;
snprintf
(
path
,
len
,
"%s/.socket%d"
,
REQUESTOR_CACHE_PATH
,
getpid
(
));
snprintf
(
path
,
len
,
"%s/.socket%d"
,
REQUESTOR_CACHE_PATH
,
(
int
)
syscall
(
SYS_gettid
));
snprintf
(
sun
.
sun_path
,
sizeof
(
sun
.
sun_path
),
"%s"
,
path
);
/*
...
...
@@ -52,8 +53,8 @@ int socket_accept(int serv_fd) {
fd_set
fds
;
int
rc
;
/* Wait
2
0 seconds for a connection, then give up. */
tv
.
tv_sec
=
2
0
;
/* Wait
6
0 seconds for a connection, then give up. */
tv
.
tv_sec
=
6
0
;
tv
.
tv_usec
=
0
;
FD_ZERO
(
&
fds
);
FD_SET
(
serv_fd
,
&
fds
);
...
...
@@ -95,7 +96,7 @@ do { \
} while (0)
void
socket_send_request
(
int
fd
,
const
struct
su_context
*
ctx
)
{
write_token
(
fd
,
"uid"
,
ctx
->
from
.
uid
);
write_token
(
fd
,
"uid"
,
ctx
->
info
->
uid
);
write_token
(
fd
,
"eof"
,
1
);
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment