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) {
...
@@ -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
)
{
void
app_send_result
(
struct
su_context
*
ctx
,
policy_t
policy
)
{
char
fromUid
[
16
];
char
fromUid
[
16
];
sprintf
(
fromUid
,
"%d"
,
ctx
->
from
.
uid
);
sprintf
(
fromUid
,
"%d"
,
ctx
->
info
->
uid
);
char
toUid
[
16
];
char
toUid
[
16
];
sprintf
(
toUid
,
"%d"
,
ctx
->
to
.
uid
);
sprintf
(
toUid
,
"%d"
,
ctx
->
to
.
uid
);
char
pid
[
16
];
char
pid
[
16
];
sprintf
(
pid
,
"%d"
,
ctx
->
from
.
pid
);
sprintf
(
pid
,
"%d"
,
ctx
->
info
->
pid
);
char
user
[
16
];
char
user
[
16
];
int
notify
=
setup_user
(
ctx
,
user
);
int
notify
=
setup_user
(
ctx
,
user
);
...
...
db.c
View file @
48ace3de
...
@@ -9,67 +9,56 @@
...
@@ -9,67 +9,56 @@
#include <sqlite3.h>
#include <sqlite3.h>
#include <time.h>
#include <time.h>
#include <string.h>
#include <string.h>
#include <unistd.h>
#include "magisk.h"
#include "magisk.h"
#include "su.h"
#include "su.h"
struct
callback_data_t
{
static
int
database_callback
(
void
*
v
,
int
argc
,
char
**
argv
,
char
**
azColName
)
{
struct
su_context
*
ctx
;
struct
su_context
*
ctx
=
(
struct
su_context
*
)
v
;
policy_t
policy
;
policy_t
policy
=
QUERY
;
};
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
;
time_t
until
=
0
;
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
(
strcmp
(
azColName
[
i
],
"policy"
)
==
0
)
{
if
(
argv
[
i
]
!=
NULL
)
{
if
(
argv
[
i
]
!=
NULL
)
{
policy
=
atoi
(
argv
[
i
]);
policy
=
atoi
(
argv
[
i
]);
}
}
}
}
else
if
(
strcmp
(
azColName
[
i
],
"until"
)
==
0
)
{
else
if
(
strcmp
(
azColName
[
i
],
"until"
)
==
0
)
{
if
(
argv
[
i
]
!=
NULL
)
{
if
(
argv
[
i
]
!=
NULL
)
{
until
=
atol
(
argv
[
i
]);
until
=
atol
(
argv
[
i
]);
}
}
}
}
}
}
if
(
policy
==
DENY
)
{
if
(
policy
==
DENY
)
data
->
policy
=
DENY
;
ctx
->
info
->
policy
=
DENY
;
return
-
1
;
else
if
(
policy
==
ALLOW
&&
(
until
==
0
||
until
>
time
(
NULL
)))
}
else
if
(
policy
==
ALLOW
&&
(
until
==
0
||
until
>
time
(
NULL
)))
{
ctx
->
info
->
policy
=
ALLOW
;
data
->
policy
=
ALLOW
;
// even though we allow, continue, so we can see if there's another policy
// that denies...
}
return
0
;
return
0
;
}
}
policy_t
database_check
(
struct
su_context
*
ctx
)
{
void
database_check
(
struct
su_context
*
ctx
)
{
sqlite3
*
db
=
NULL
;
sqlite3
*
db
=
NULL
;
// Check if file is readable
if
(
access
(
ctx
->
user
.
database_path
,
R_OK
)
==
-
1
)
return
;
char
query
[
512
];
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
);
int
ret
=
sqlite3_open_v2
(
ctx
->
user
.
database_path
,
&
db
,
SQLITE_OPEN_READONLY
,
NULL
);
if
(
ret
)
{
if
(
ret
)
{
LOG
E
(
"sqlite3 open failure: %d"
,
ret
);
LOG
D
(
"sqlite3 open failure: %s
\n
"
,
sqlite3_errstr
(
ret
)
);
sqlite3_close
(
db
);
sqlite3_close
(
db
);
return
INTERACTIVE
;
return
;
}
}
char
*
err
=
NULL
;
char
*
err
=
NULL
;
struct
callback_data_t
data
;
ret
=
sqlite3_exec
(
db
,
query
,
database_callback
,
ctx
,
&
err
);
data
.
ctx
=
ctx
;
data
.
policy
=
INTERACTIVE
;
ret
=
sqlite3_exec
(
db
,
query
,
database_callback
,
&
data
,
&
err
);
sqlite3_close
(
db
);
sqlite3_close
(
db
);
if
(
err
!=
NULL
)
{
if
(
err
!=
NULL
)
{
LOGE
(
"sqlite3_exec: %s"
,
err
);
LOGE
(
"sqlite3_exec: %s
\n
"
,
err
);
return
DENY
;
ctx
->
info
->
policy
=
DENY
;
}
}
return
data
.
policy
;
}
}
su.c
View file @
48ace3de
...
@@ -27,7 +27,7 @@
...
@@ -27,7 +27,7 @@
#include "resetprop.h"
#include "resetprop.h"
#include "su.h"
#include "su.h"
st
atic
st
ruct
su_context
*
su_ctx
;
struct
su_context
*
su_ctx
;
static
void
usage
(
int
status
)
{
static
void
usage
(
int
status
)
{
FILE
*
stream
=
(
status
==
EXIT_SUCCESS
)
?
stdout
:
stderr
;
FILE
*
stream
=
(
status
==
EXIT_SUCCESS
)
?
stdout
:
stderr
;
...
@@ -46,7 +46,7 @@ static void usage(int status) {
...
@@ -46,7 +46,7 @@ static void usage(int status) {
" -v, --version display version number and exit
\n
"
" -v, --version display version number and exit
\n
"
" -V display version code and exit,
\n
"
" -V display version code and exit,
\n
"
" this is used almost exclusively by Superuser.apk
\n
"
);
" this is used almost exclusively by Superuser.apk
\n
"
);
exit
(
status
);
exit
2
(
status
);
}
}
static
char
*
concat_commands
(
int
argc
,
char
*
argv
[])
{
static
char
*
concat_commands
(
int
argc
,
char
*
argv
[])
{
...
@@ -61,16 +61,6 @@ static char *concat_commands(int argc, char *argv[]) {
...
@@ -61,16 +61,6 @@ static char *concat_commands(int argc, char *argv[]) {
return
strdup
(
command
);
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
)
{
static
void
populate_environment
(
const
struct
su_context
*
ctx
)
{
struct
passwd
*
pw
;
struct
passwd
*
pw
;
...
@@ -96,8 +86,7 @@ static __attribute__ ((noreturn)) void allow() {
...
@@ -96,8 +86,7 @@ static __attribute__ ((noreturn)) void allow() {
umask
(
su_ctx
->
umask
);
umask
(
su_ctx
->
umask
);
// no need to log if called by root
if
(
su_ctx
->
notify
)
if
(
su_ctx
->
from
.
uid
!=
UID_ROOT
)
app_send_result
(
su_ctx
,
ALLOW
);
app_send_result
(
su_ctx
,
ALLOW
);
char
*
binary
=
su_ctx
->
to
.
shell
;
char
*
binary
=
su_ctx
->
to
.
shell
;
...
@@ -129,11 +118,10 @@ static __attribute__ ((noreturn)) void allow() {
...
@@ -129,11 +118,10 @@ static __attribute__ ((noreturn)) void allow() {
}
}
static
__attribute__
((
noreturn
))
void
deny
()
{
static
__attribute__
((
noreturn
))
void
deny
()
{
// no need to log if called by root
if
(
su_ctx
->
notify
)
if
(
su_ctx
->
from
.
uid
!=
UID_ROOT
)
app_send_result
(
su_ctx
,
DENY
);
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
));
fprintf
(
stderr
,
"%s
\n
"
,
strerror
(
EACCES
));
exit
(
EXIT_FAILURE
);
exit
(
EXIT_FAILURE
);
}
}
...
@@ -147,7 +135,17 @@ static void socket_cleanup() {
...
@@ -147,7 +135,17 @@ static void socket_cleanup() {
static
void
cleanup_signal
(
int
sig
)
{
static
void
cleanup_signal
(
int
sig
)
{
socket_cleanup
();
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
)
{
int
su_daemon_main
(
int
argc
,
char
**
argv
)
{
...
@@ -199,35 +197,8 @@ 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
;
int
c
,
socket_serv_fd
,
fd
;
char
result
[
64
];
char
result
[
64
];
policy_t
dballow
;
struct
option
long_opts
[]
=
{
struct
option
long_opts
[]
=
{
{
"command"
,
required_argument
,
NULL
,
'c'
},
{
"command"
,
required_argument
,
NULL
,
'c'
},
{
"help"
,
no_argument
,
NULL
,
'h'
},
{
"help"
,
no_argument
,
NULL
,
'h'
},
...
@@ -242,30 +213,31 @@ int su_daemon_main(int argc, char **argv) {
...
@@ -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
)
{
while
((
c
=
getopt_long
(
argc
,
argv
,
"c:hlmps:Vvuz:"
,
long_opts
,
NULL
))
!=
-
1
)
{
switch
(
c
)
{
switch
(
c
)
{
case
'c'
:
case
'c'
:
ctx
.
to
.
command
=
concat_commands
(
argc
,
argv
);
su_ctx
->
to
.
command
=
concat_commands
(
argc
,
argv
);
optind
=
argc
;
optind
=
argc
;
su_ctx
->
notify
=
1
;
break
;
break
;
case
'h'
:
case
'h'
:
usage
(
EXIT_SUCCESS
);
usage
(
EXIT_SUCCESS
);
break
;
break
;
case
'l'
:
case
'l'
:
ctx
.
to
.
login
=
1
;
su_ctx
->
to
.
login
=
1
;
break
;
break
;
case
'm'
:
case
'm'
:
case
'p'
:
case
'p'
:
ctx
.
to
.
keepenv
=
1
;
su_ctx
->
to
.
keepenv
=
1
;
break
;
break
;
case
's'
:
case
's'
:
ctx
.
to
.
shell
=
optarg
;
su_ctx
->
to
.
shell
=
optarg
;
break
;
break
;
case
'V'
:
case
'V'
:
printf
(
"%d
\n
"
,
MAGISK_VER_CODE
);
printf
(
"%d
\n
"
,
MAGISK_VER_CODE
);
exit
(
EXIT_SUCCESS
);
exit
2
(
EXIT_SUCCESS
);
case
'v'
:
case
'v'
:
printf
(
"%s
\n
"
,
MAGISKSU_VER_STR
);
printf
(
"%s
\n
"
,
MAGISKSU_VER_STR
);
exit
(
EXIT_SUCCESS
);
exit
2
(
EXIT_SUCCESS
);
case
'u'
:
case
'u'
:
switch
(
ctx
.
user
.
multiuser_mode
)
{
switch
(
su_ctx
->
user
.
multiuser_mode
)
{
case
MULTIUSER_MODE_USER
:
case
MULTIUSER_MODE_USER
:
printf
(
"Owner only: Only owner has root access
\n
"
);
printf
(
"Owner only: Only owner has root access
\n
"
);
break
;
break
;
...
@@ -276,7 +248,7 @@ int su_daemon_main(int argc, char **argv) {
...
@@ -276,7 +248,7 @@ int su_daemon_main(int argc, char **argv) {
printf
(
"User independent: Each user has its own separate root rules
\n
"
);
printf
(
"User independent: Each user has its own separate root rules
\n
"
);
break
;
break
;
}
}
exit
(
EXIT_SUCCESS
);
exit
2
(
EXIT_SUCCESS
);
case
'z'
:
case
'z'
:
// Do nothing, placed here for legacy support :)
// Do nothing, placed here for legacy support :)
break
;
break
;
...
@@ -286,8 +258,12 @@ int su_daemon_main(int argc, char **argv) {
...
@@ -286,8 +258,12 @@ int su_daemon_main(int argc, char **argv) {
usage
(
2
);
usage
(
2
);
}
}
}
}
su_ctx
->
to
.
argc
=
argc
;
su_ctx
->
to
.
argv
=
argv
;
if
(
optind
<
argc
&&
!
strcmp
(
argv
[
optind
],
"-"
))
{
if
(
optind
<
argc
&&
!
strcmp
(
argv
[
optind
],
"-"
))
{
ctx
.
to
.
login
=
1
;
su_ctx
->
to
.
login
=
1
;
optind
++
;
optind
++
;
}
}
/* username or uid */
/* username or uid */
...
@@ -299,14 +275,14 @@ int su_daemon_main(int argc, char **argv) {
...
@@ -299,14 +275,14 @@ int su_daemon_main(int argc, char **argv) {
/* It seems we shouldn't do this at all */
/* It seems we shouldn't do this at all */
errno
=
0
;
errno
=
0
;
ctx
.
to
.
uid
=
strtoul
(
argv
[
optind
],
&
endptr
,
10
);
su_ctx
->
to
.
uid
=
strtoul
(
argv
[
optind
],
&
endptr
,
10
);
if
(
errno
||
*
endptr
)
{
if
(
errno
||
*
endptr
)
{
LOGE
(
"Unknown id: %s
\n
"
,
argv
[
optind
]);
LOGE
(
"Unknown id: %s
\n
"
,
argv
[
optind
]);
fprintf
(
stderr
,
"Unknown id: %s
\n
"
,
argv
[
optind
]);
fprintf
(
stderr
,
"Unknown id: %s
\n
"
,
argv
[
optind
]);
exit
(
EXIT_FAILURE
);
exit
(
EXIT_FAILURE
);
}
}
}
else
{
}
else
{
ctx
.
to
.
uid
=
pw
->
pw_uid
;
su_ctx
->
to
.
uid
=
pw
->
pw_uid
;
}
}
optind
++
;
optind
++
;
}
}
...
@@ -314,54 +290,28 @@ int su_daemon_main(int argc, char **argv) {
...
@@ -314,54 +290,28 @@ int su_daemon_main(int argc, char **argv) {
optind
++
;
optind
++
;
}
}
//
The su_context setup is
done, now every error leads to deny
//
Setup
done, now every error leads to deny
err_handler
=
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
// Check property of root configuration
char
*
root_prop
=
getprop
(
ROOT_ACCESS_PROP
);
char
*
root_prop
=
getprop
(
ROOT_ACCESS_PROP
);
if
(
root_prop
)
{
if
(
root_prop
)
{
int
prop_status
=
atoi
(
root_prop
);
int
prop_status
=
atoi
(
root_prop
);
switch
(
prop_status
)
{
switch
(
prop_status
)
{
case
ROOT_ACCESS_DISABLED
:
case
ROOT_ACCESS_DISABLED
:
LOGE
(
"Root access is disabled!
\n
"
);
exit
(
EXIT_FAILURE
);
exit
(
EXIT_FAILURE
);
case
ROOT_ACCESS_APPS_ONLY
:
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
);
exit
(
EXIT_FAILURE
);
}
break
;
break
;
case
ROOT_ACCESS_ADB_ONLY
:
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
);
exit
(
EXIT_FAILURE
);
}
break
;
break
;
case
ROOT_ACCESS_APPS_AND_ADB
:
case
ROOT_ACCESS_APPS_AND_ADB
:
default
:
default
:
...
@@ -373,62 +323,38 @@ int su_daemon_main(int argc, char **argv) {
...
@@ -373,62 +323,38 @@ int su_daemon_main(int argc, char **argv) {
setprop
(
ROOT_ACCESS_PROP
,
xstr
(
ROOT_ACCESS_APPS_AND_ADB
));
setprop
(
ROOT_ACCESS_PROP
,
xstr
(
ROOT_ACCESS_APPS_AND_ADB
));
}
}
//
Allow root to start root
//
New request or no db exist, notify user for response
if
(
ctx
.
from
.
uid
==
UID_ROOT
)
{
if
(
su_ctx
->
info
->
policy
==
QUERY
)
{
allow
(
);
socket_serv_fd
=
socket_create_temp
(
su_ctx
->
sock_path
,
sizeof
(
su_ctx
->
sock_path
)
);
}
setup_sighandlers
(
cleanup_signal
);
mkdir
(
REQUESTOR_CACHE_PATH
,
0770
);
// Start activity
if
(
chown
(
REQUESTOR_CACHE_PATH
,
st
.
st_uid
,
st
.
st_gid
))
{
app_send_request
(
su_ctx
);
PLOGE
(
"chown (%s, %u, %u)"
,
REQUESTOR_CACHE_PATH
,
st
.
st_uid
,
st
.
st_gid
);
}
if
(
setgroups
(
0
,
NULL
))
{
atexit
(
socket_cleanup
);
PLOGE
(
"setgroups"
);
}
if
(
setegid
(
st
.
st_gid
))
{
PLOGE
(
"setegid (%u)"
,
st
.
st_gid
);
}
if
(
seteuid
(
st
.
st_uid
))
{
PLOGE
(
"seteuid (%u)"
,
st
.
st_uid
);
}
// If db exits, check directly instead query application
fd
=
socket_accept
(
socket_serv_fd
);
dballow
=
database_check
(
&
ctx
);
socket_send_request
(
fd
,
su_ctx
);
switch
(
dballow
)
{
socket_receive_result
(
fd
,
result
,
sizeof
(
result
));
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
);
// Start activity
close
(
fd
);
app_send_request
(
su_ctx
);
close
(
socket_serv_fd
);
socket_cleanup
();
atexit
(
socket_cleanup
);
if
(
strcmp
(
result
,
"socket:ALLOW"
)
==
0
)
su_ctx
->
info
->
policy
=
ALLOW
;
fd
=
socket_accept
(
socket_serv_fd
);
else
socket_send_request
(
fd
,
&
ctx
);
su_ctx
->
info
->
policy
=
DENY
;
socket_receive_result
(
fd
,
result
,
sizeof
(
result
));
close
(
fd
);
// Report the policy to main daemon
close
(
socket_serv_fd
);
xwrite
(
pipefd
[
1
],
&
su_ctx
->
info
->
policy
,
sizeof
(
su_ctx
->
info
->
policy
));
socket_cleanup
();
close
(
pipefd
[
0
]);
close
(
pipefd
[
1
]);
}
if
(
strcmp
(
result
,
"socket:DENY"
)
==
0
)
{
if
(
su_ctx
->
info
->
policy
==
ALLOW
)
deny
();
}
else
if
(
strcmp
(
result
,
"socket:ALLOW"
)
==
0
)
{
allow
();
allow
();
}
else
{
else
LOGE
(
"unknown response from Superuser Requestor: %s"
,
result
);
deny
();
deny
();
}
}
}
su.h
View file @
48ace3de
...
@@ -7,6 +7,8 @@
...
@@ -7,6 +7,8 @@
#include <limits.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/types.h>
#include "list.h"
#define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)"
#define MAGISKSU_VER_STR xstr(MAGISK_VERSION) ":MAGISKSU (topjohnwu)"
// Property check for root access
// Property check for root access
...
@@ -43,9 +45,20 @@
...
@@ -43,9 +45,20 @@
#define DEFAULT_SHELL "/system/bin/sh"
#define DEFAULT_SHELL "/system/bin/sh"
typedef
enum
{
QUERY
=
0
,
DENY
=
1
,
ALLOW
=
2
,
}
policy_t
;
struct
su_initiator
{
struct
su_initiator
{
pid_t
pid
;
pid_t
pid
;
unsigned
uid
;
unsigned
uid
;
policy_t
policy
;
pthread_mutex_t
lock
;
int
count
;
int
clock
;
struct
list_head
pos
;
};
};
struct
su_request
{
struct
su_request
{
...
@@ -76,24 +89,21 @@ struct su_user_info {
...
@@ -76,24 +89,21 @@ struct su_user_info {
};
};
struct
su_context
{
struct
su_context
{
struct
su_initiator
from
;
struct
su_initiator
*
info
;
struct
su_request
to
;
struct
su_request
to
;
struct
su_user_info
user
;
struct
su_user_info
user
;
int
notify
;
mode_t
umask
;
mode_t
umask
;
char
sock_path
[
PATH_MAX
];
char
sock_path
[
PATH_MAX
];
};
};
typedef
enum
{
extern
struct
su_context
*
su_ctx
;
INTERACTIVE
=
0
,
extern
int
pipefd
[
2
];
DENY
=
1
,
ALLOW
=
2
,
}
policy_t
;
extern
struct
ucred
su_credentials
;
// su.c
// su.c
int
su_daemon_main
(
int
argc
,
char
**
argv
);
int
su_daemon_main
(
int
argc
,
char
**
argv
);
__attribute__
((
noreturn
))
void
exit2
(
int
status
);
// su_client.c
// su_client.c
...
@@ -109,7 +119,7 @@ void app_send_request(struct su_context *ctx);
...
@@ -109,7 +119,7 @@ void app_send_request(struct su_context *ctx);
// db.c
// db.c
policy_t
database_check
(
struct
su_context
*
ctx
);
void
database_check
(
struct
su_context
*
ctx
);
// misc.c
// 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
/* su_client.c - The entrypoint for su, connect to daemon and send correct info
*/
*/
#define _GNU_SOURCE
#include <limits.h>
#include <limits.h>
#include <unistd.h>
#include <unistd.h>
#include <pthread.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <fcntl.h>
#include <string.h>
#include <string.h>
#include <signal.h>
#include <signal.h>
...
@@ -16,17 +17,24 @@
...
@@ -16,17 +17,24 @@
#include "magisk.h"
#include "magisk.h"
#include "daemon.h"
#include "daemon.h"
#include "resetprop.h"
#include "utils.h"
#include "utils.h"
#include "su.h"
#include "su.h"
#include "pts.h"
#include "pts.h"
#include "list.h"
// Constants for the atty bitfield
// Constants for the atty bitfield
#define ATTY_IN 1
#define ATTY_IN 1
#define ATTY_OUT 2
#define ATTY_OUT 2
#define ATTY_ERR 4
#define ATTY_ERR 4
struct
ucred
su_credentials
;
#define TIMEOUT 3
static
__thread
int
client_fd
;
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
)
{
static
void
sighandler
(
int
sig
)
{
restore_stdin
();
restore_stdin
();
...
@@ -53,27 +61,181 @@ static void sighandler(int sig) {
...
@@ -53,27 +61,181 @@ static void sighandler(int sig) {
static
void
sigpipe_handler
(
int
sig
)
{
static
void
sigpipe_handler
(
int
sig
)
{
LOGD
(
"su: Client killed unexpectedly
\n
"
);
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
)
{
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,
// Fork a new process, the child process will need to setsid,
// open a pseudo-terminal if needed, and will eventually run exec
// open a pseudo-terminal if needed, and will eventually run exec
// The parent process will wait for the result and
// The parent process will wait for the result and
// send the return code back to our client
// send the return code back to our client
int
child
=
fork
();
int
child
=
fork
();
if
(
child
<
0
)
{
if
(
child
<
0
)
write
(
client
,
&
child
,
sizeof
(
child
));
close
(
client
);
PLOGE
(
"fork"
);
PLOGE
(
"fork"
);
return
;
}
else
if
(
child
!=
0
)
{
// For sighandler to close it
if
(
child
)
{
client_fd
=
client
;
// 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
// Wait result
LOGD
(
"su: wait_result waiting for %d
\n
"
,
child
);
LOGD
(
"su: wait_result waiting for %d
\n
"
,
child
);
...
@@ -91,10 +253,13 @@ void su_daemon_receiver(int client) {
...
@@ -91,10 +253,13 @@ void su_daemon_receiver(int client) {
code
=
-
1
;
code
=
-
1
;
// Pass the return code back to the client
// 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
);
LOGD
(
"su: return code to client: %d
\n
"
,
code
);
close
(
client
);
close
(
client
);
// Decrement reference count
--
info
->
count
;
return
;
return
;
}
}
...
@@ -106,14 +271,11 @@ void su_daemon_receiver(int client) {
...
@@ -106,14 +271,11 @@ void su_daemon_receiver(int client) {
// Become session leader
// Become session leader
xsetsid
();
xsetsid
();
// Get the credentials
get_client_cred
(
client
,
&
su_credentials
);
// Let's read some info from the socket
// Let's read some info from the socket
int
argc
=
read_int
(
client
);
int
argc
=
read_int
(
client
);
if
(
argc
<
0
||
argc
>
512
)
{
if
(
argc
<
0
||
argc
>
512
)
{
LOGE
(
"unable to allocate args: %d"
,
argc
);
LOGE
(
"unable to allocate args: %d"
,
argc
);
exit
(
1
);
exit
2
(
1
);
}
}
LOGD
(
"su: argc=[%d]
\n
"
,
argc
);
LOGD
(
"su: argc=[%d]
\n
"
,
argc
);
...
@@ -149,9 +311,9 @@ void su_daemon_receiver(int client) {
...
@@ -149,9 +311,9 @@ void su_daemon_receiver(int client) {
xstat
(
pts_slave
,
&
stbuf
);
xstat
(
pts_slave
,
&
stbuf
);
//If caller is not root, ensure the owner of pts_slave is the caller
//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"
);
LOGE
(
"su: Wrong permission of pts_slave"
);
exit
(
1
);
exit
2
(
1
);
}
}
// Opening the TTY has to occur after the
// Opening the TTY has to occur after the
...
@@ -182,6 +344,20 @@ void su_daemon_receiver(int client) {
...
@@ -182,6 +344,20 @@ void su_daemon_receiver(int client) {
close
(
ptsfd
);
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_daemon_main
(
argc
,
argv
);
}
}
...
...
su_socket.c
View file @
48ace3de
...
@@ -12,6 +12,7 @@
...
@@ -12,6 +12,7 @@
#include <sys/socket.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/un.h>
#include <selinux/selinux.h>
#include <selinux/selinux.h>
#include <syscall.h>
#include "magisk.h"
#include "magisk.h"
#include "utils.h"
#include "utils.h"
...
@@ -28,7 +29,7 @@ int socket_create_temp(char *path, size_t len) {
...
@@ -28,7 +29,7 @@ int socket_create_temp(char *path, size_t len) {
memset
(
&
sun
,
0
,
sizeof
(
sun
));
memset
(
&
sun
,
0
,
sizeof
(
sun
));
sun
.
sun_family
=
AF_LOCAL
;
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
);
snprintf
(
sun
.
sun_path
,
sizeof
(
sun
.
sun_path
),
"%s"
,
path
);
/*
/*
...
@@ -52,8 +53,8 @@ int socket_accept(int serv_fd) {
...
@@ -52,8 +53,8 @@ int socket_accept(int serv_fd) {
fd_set
fds
;
fd_set
fds
;
int
rc
;
int
rc
;
/* Wait
2
0 seconds for a connection, then give up. */
/* Wait
6
0 seconds for a connection, then give up. */
tv
.
tv_sec
=
2
0
;
tv
.
tv_sec
=
6
0
;
tv
.
tv_usec
=
0
;
tv
.
tv_usec
=
0
;
FD_ZERO
(
&
fds
);
FD_ZERO
(
&
fds
);
FD_SET
(
serv_fd
,
&
fds
);
FD_SET
(
serv_fd
,
&
fds
);
...
@@ -95,7 +96,7 @@ do { \
...
@@ -95,7 +96,7 @@ do { \
} while (0)
} while (0)
void
socket_send_request
(
int
fd
,
const
struct
su_context
*
ctx
)
{
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_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