Skip to content

Commit

Permalink
fix: thread config.yml daemon pem-db config through ipsw API ro…
Browse files Browse the repository at this point in the history
…utes that can use it
  • Loading branch information
blacktop committed Jul 16, 2024
1 parent a342667 commit 24a64fc
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 134 deletions.
264 changes: 141 additions & 123 deletions api/server/routes/ipsw/ipsw.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,99 +40,105 @@ type getFsFilesResponse struct {
Files []File `json:"files"`
}

func getFsFiles(c *gin.Context) {
ipswPath, ok := c.GetQuery("path")
if !ok {
c.AbortWithStatusJSON(http.StatusBadRequest, types.GenericError{Error: "missing path query parameter"})
return
} else {
ipswPath = filepath.Clean(ipswPath)
}
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
}

i, err := info.Parse(ipswPath)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}
dmgPath, err := i.GetFileSystemOsDmg()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}
if _, err := os.Stat(dmgPath); os.IsNotExist(err) {
// extract filesystem DMG
dmgs, err := utils.Unzip(ipswPath, "", func(f *zip.File) bool {
return strings.EqualFold(filepath.Base(f.Name), dmgPath)
})
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to extract %s from IPSW: %v", dmgPath, err)})
func getFsFiles(pemDB string) gin.HandlerFunc {
return func(c *gin.Context) {
ipswPath, ok := c.GetQuery("path")
if !ok {
c.AbortWithStatusJSON(http.StatusBadRequest, types.GenericError{Error: "missing path query parameter"})
return
} else {
ipswPath = filepath.Clean(ipswPath)
}
if len(dmgs) == 0 {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to find %s in IPSW", dmgPath)})
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
} else {
if pemDB != "" {
pemDbPath = filepath.Clean(pemDB)
}
}
defer os.Remove(filepath.Clean(dmgs[0]))
} else {
utils.Indent(log.Debug, 2)(fmt.Sprintf("Found extracted %s", dmgPath))
}

if filepath.Ext(dmgPath) == ".aea" {
dmgPath, err = aea.Decrypt(dmgPath, filepath.Dir(dmgPath), nil, pemDbPath)
i, err := info.Parse(ipswPath)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to parse AEA encrypted DMG: %v", err)})
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}
}

// mount filesystem DMG
utils.Indent(log.Info, 2)(fmt.Sprintf("Mounting %s", dmgPath))
mountPoint, alreadyMounted, err := utils.MountDMG(dmgPath)
if err != nil {
if !errors.Is(err, utils.ErrMountResourceBusy) {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to mount DMG: %v", err)})
dmgPath, err := i.GetFileSystemOsDmg()
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}
}
if alreadyMounted {
utils.Indent(log.Info, 3)(fmt.Sprintf("%s already mounted", dmgPath))
} else {
defer func() {
utils.Indent(log.Info, 2)(fmt.Sprintf("Unmounting %s", dmgPath))
if err := utils.Retry(3, 2*time.Second, func() error {
return utils.Unmount(mountPoint, false)
}); err != nil {
log.Errorf("failed to unmount %s at %s: %v", dmgPath, mountPoint, err)
if _, err := os.Stat(dmgPath); os.IsNotExist(err) {
// extract filesystem DMG
dmgs, err := utils.Unzip(ipswPath, "", func(f *zip.File) bool {
return strings.EqualFold(filepath.Base(f.Name), dmgPath)
})
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to extract %s from IPSW: %v", dmgPath, err)})
}
}()
}
if len(dmgs) == 0 {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to find %s in IPSW", dmgPath)})
}
defer os.Remove(filepath.Clean(dmgs[0]))
} else {
utils.Indent(log.Debug, 2)(fmt.Sprintf("Found extracted %s", dmgPath))
}

if filepath.Ext(dmgPath) == ".aea" {
dmgPath, err = aea.Decrypt(dmgPath, filepath.Dir(dmgPath), nil, pemDbPath)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to parse AEA encrypted DMG: %v", err)})
}
}

var files []File
if err := filepath.Walk(mountPoint, func(path string, info fs.FileInfo, err error) error {
// mount filesystem DMG
utils.Indent(log.Info, 2)(fmt.Sprintf("Mounting %s", dmgPath))
mountPoint, alreadyMounted, err := utils.MountDMG(dmgPath)
if err != nil {
return fmt.Errorf("prevent panic by handling failure accessing a path %q: %v", path, err)
if !errors.Is(err, utils.ErrMountResourceBusy) {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to mount DMG: %v", err)})
}
}
if alreadyMounted {
utils.Indent(log.Info, 3)(fmt.Sprintf("%s already mounted", dmgPath))
} else {
defer func() {
utils.Indent(log.Info, 2)(fmt.Sprintf("Unmounting %s", dmgPath))
if err := utils.Retry(3, 2*time.Second, func() error {
return utils.Unmount(mountPoint, false)
}); err != nil {
log.Errorf("failed to unmount %s at %s: %v", dmgPath, mountPoint, err)
}
}()
}
if info.IsDir() {

var files []File
if err := filepath.Walk(mountPoint, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("prevent panic by handling failure accessing a path %q: %v", path, err)
}
if info.IsDir() {
return nil
// return filepath.SkipDir
}
fpath, err := filepath.Rel(mountPoint, path)
if err != nil {
return fmt.Errorf("failed to get relative path for %s: %v", path, err)
}
files = append(files, File{
Name: fpath,
Size: info.Size(),
Mode: info.Mode().String(),
ModTime: info.ModTime(),
})
return nil
// return filepath.SkipDir
}); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}
fpath, err := filepath.Rel(mountPoint, path)
if err != nil {
return fmt.Errorf("failed to get relative path for %s: %v", path, err)
}
files = append(files, File{
Name: fpath,
Size: info.Size(),
Mode: info.Mode().String(),
ModTime: info.ModTime(),
})
return nil
}); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}

c.IndentedJSON(http.StatusOK, getFsFilesResponse{Path: ipswPath, Files: files})
c.IndentedJSON(http.StatusOK, getFsFilesResponse{Path: ipswPath, Files: files})
}
}

// swagger:response
Expand All @@ -141,36 +147,42 @@ type getFsEntitlementsResponse struct {
Entitlements map[string]map[string]any `json:"entitlements"`
}

func getFsEntitlements(c *gin.Context) {
ipswPath, ok := c.GetQuery("path")
if !ok {
c.AbortWithStatusJSON(http.StatusBadRequest, types.GenericError{Error: "missing path query parameter"})
return
} else {
ipswPath = filepath.Clean(ipswPath)
}
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
}
func getFsEntitlements(pemDB string) gin.HandlerFunc {
return func(c *gin.Context) {
ipswPath, ok := c.GetQuery("path")
if !ok {
c.AbortWithStatusJSON(http.StatusBadRequest, types.GenericError{Error: "missing path query parameter"})
return
} else {
ipswPath = filepath.Clean(ipswPath)
}
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
} else {
if pemDB != "" {
pemDbPath = filepath.Clean(pemDB)
}
}

ents, err := ent.GetDatabase(&ent.Config{IPSW: ipswPath, PemDB: pemDbPath})
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}
ents, err := ent.GetDatabase(&ent.Config{IPSW: ipswPath, PemDB: pemDbPath})
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}

entDB := make(map[string]map[string]any)
entDB := make(map[string]map[string]any)

for f, ent := range ents {
ents := make(map[string]any)
if err := plist.NewDecoder(bytes.NewReader([]byte(ent))).Decode(&ents); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to decode entitlements plist for %s: %v", f, err)})
for f, ent := range ents {
ents := make(map[string]any)
if err := plist.NewDecoder(bytes.NewReader([]byte(ent))).Decode(&ents); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: fmt.Sprintf("failed to decode entitlements plist for %s: %v", f, err)})
}
entDB[f] = ents
}
entDB[f] = ents
}

c.IndentedJSON(http.StatusOK, getFsEntitlementsResponse{Path: ipswPath, Entitlements: entDB})
c.IndentedJSON(http.StatusOK, getFsEntitlementsResponse{Path: ipswPath, Entitlements: entDB})
}
}

// swagger:response
Expand All @@ -179,24 +191,30 @@ type getFsLaunchdConfigResponse struct {
LaunchdConfig string `json:"launchd_config"`
}

func getFsLaunchdConfig(c *gin.Context) {
ipswPath, ok := c.GetQuery("path")
if !ok {
c.AbortWithStatusJSON(http.StatusBadRequest, types.GenericError{Error: "missing path query parameter"})
return
} else {
ipswPath = filepath.Clean(ipswPath)
}
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
}
func getFsLaunchdConfig(pemDB string) gin.HandlerFunc {
return func(c *gin.Context) {
ipswPath, ok := c.GetQuery("path")
if !ok {
c.AbortWithStatusJSON(http.StatusBadRequest, types.GenericError{Error: "missing path query parameter"})
return
} else {
ipswPath = filepath.Clean(ipswPath)
}
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
} else {
if pemDB != "" {
pemDbPath = filepath.Clean(pemDB)
}
}

ldconf, err := extract.LaunchdConfig(ipswPath, pemDbPath)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}
ldconf, err := extract.LaunchdConfig(ipswPath, pemDbPath)
if err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
return
}

c.IndentedJSON(http.StatusOK, getFsLaunchdConfigResponse{Path: ipswPath, LaunchdConfig: ldconf})
c.IndentedJSON(http.StatusOK, getFsLaunchdConfigResponse{Path: ipswPath, LaunchdConfig: ldconf})
}
}
8 changes: 4 additions & 4 deletions api/server/routes/ipsw/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

// AddRoutes adds the download routes to the router
func AddRoutes(rg *gin.RouterGroup) {
func AddRoutes(rg *gin.RouterGroup, pemDB string) {
dl := rg.Group("/ipsw")
// swagger:route GET /ipsw/fs/files IPSW getIpswFsFiles
//
Expand All @@ -31,7 +31,7 @@ func AddRoutes(rg *gin.RouterGroup) {
// Responses:
// 200: getFsFilesResponse
// 500: genericError
dl.GET("/fs/files", getFsFiles)
dl.GET("/fs/files", getFsFiles(pemDB))
// swagger:route GET /ipsw/fs/ents IPSW getIpswFsEntitlements
//
// Entitlements
Expand All @@ -56,7 +56,7 @@ func AddRoutes(rg *gin.RouterGroup) {
// Responses:
// 200: getFsEntitlementsResponse
// 500: genericError
dl.GET("/fs/ents", getFsEntitlements)
dl.GET("/fs/ents", getFsEntitlements(pemDB))
// swagger:route GET /ipsw/fs/launchd IPSW getIpswFsLaunchd
//
// launchd Config
Expand All @@ -81,5 +81,5 @@ func AddRoutes(rg *gin.RouterGroup) {
// Responses:
// 200: getFsLaunchdConfigResponse
// 500: genericError
dl.GET("/fs/launchd", getFsLaunchdConfig)
dl.GET("/fs/launchd", getFsLaunchdConfig(pemDB))
}
6 changes: 5 additions & 1 deletion api/server/routes/mount/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type successResponse struct {
}

// AddRoutes adds the download routes to the router
func AddRoutes(rg *gin.RouterGroup) {
func AddRoutes(rg *gin.RouterGroup, pemDB string) {
// swagger:route POST /mount/{type} Mount postMount
//
// Mount
Expand Down Expand Up @@ -64,6 +64,10 @@ func AddRoutes(rg *gin.RouterGroup) {
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
} else {
if pemDB != "" {
pemDbPath = filepath.Clean(pemDB)
}
}
dmgType := c.Param("type")
if !utils.StrSliceContains([]string{"app", "sys", "fs"}, dmgType) {
Expand Down
6 changes: 3 additions & 3 deletions api/server/routes/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
)

// Add adds the command routes to the router
func Add(rg *gin.RouterGroup) {
func Add(rg *gin.RouterGroup, pemDB string) {
daemon.AddRoutes(rg)
devicelist.AddRoutes(rg)
diff.AddRoutes(rg)
Expand All @@ -29,11 +29,11 @@ func Add(rg *gin.RouterGroup) {
idev.AddRoutes(rg)
// img4.AddRoutes(rg) // TODO: add img4 routes
info.AddRoutes(rg)
ipsw.AddRoutes(rg)
ipsw.AddRoutes(rg, pemDB)
kernel.AddRoutes(rg)
macho.AddRoutes(rg)
// mdevs.AddRoutes(rg) // TODO: add mdevs routes
mount.AddRoutes(rg)
mount.AddRoutes(rg, pemDB)
// ota.AddRoutes(rg) // TODO: add ota routes
// pongo.AddRoutes(rg) // TODO: add pongo routes
// sepfw.AddRoutes(rg) // TODO: add sepfw routes
Expand Down
6 changes: 5 additions & 1 deletion api/server/routes/syms/syms.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type IpswParams struct {
}

// AddRoutes adds the syms routes to the router
func AddRoutes(rg *gin.RouterGroup, db db.Database) {
func AddRoutes(rg *gin.RouterGroup, db db.Database, pemDB string) {
// swagger:route POST /syms/scan Syms postScan
//
// Scan
Expand Down Expand Up @@ -76,6 +76,10 @@ func AddRoutes(rg *gin.RouterGroup, db db.Database) {
pemDbPath, ok := c.GetQuery("pem_db")
if ok {
pemDbPath = filepath.Clean(pemDbPath)
} else {
if pemDB != "" {
pemDbPath = filepath.Clean(pemDB)
}
}
if err := syms.Scan(ipswPath, pemDbPath, db); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, types.GenericError{Error: err.Error()})
Expand Down
Loading

0 comments on commit 24a64fc

Please sign in to comment.