mirror of
https://github.com/blacktop/ipsw.git
synced 2026-05-08 12:22:26 +00:00
c20f9af712
- Remove POST /diff/files endpoint that allowed arbitrary host file reads (CWE-22); clients must now read files locally and POST content to /diff/blobs instead - Add validatePublicURL() to /info remote endpoints, rejecting URLs that resolve to loopback, private, link-local, or multicast addresses to prevent SSRF; also remove attacker-controlled proxy/insecure params - Add SanitizeArchivePath() helper that verifies extracted archive entry paths stay within the destination directory (zip-slip / tar-slip, CWE-22); replace bare filepath.Join(dest, filepath.Clean(name)) calls in SearchZip, OTA parser, AA payload extractor, and ota_extract - Fix server listen address to use net.JoinHostPort to respect Host config
155 lines
3.4 KiB
Go
155 lines
3.4 KiB
Go
// Package server ipswd API
|
|
//
|
|
// This allows you to interact with <code>ipsw</code> in a VERY powerful and flexible way via a RESTful API.
|
|
//
|
|
// The <code>ipswd</code> design was heavily influenced by the design of dockerd. So many of the same concepts apply.
|
|
//
|
|
// Schemes: http
|
|
// Host: localhost:3993
|
|
// BasePath: /v1
|
|
// Version: v1.0
|
|
//
|
|
// Consumes:
|
|
// - application/json
|
|
//
|
|
// Produces:
|
|
// - application/json
|
|
//
|
|
// swagger:meta
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/apex/log"
|
|
"github.com/blacktop/ipsw/api"
|
|
"github.com/blacktop/ipsw/api/server/routes"
|
|
"github.com/blacktop/ipsw/api/server/routes/aea"
|
|
"github.com/blacktop/ipsw/api/server/routes/syms"
|
|
"github.com/blacktop/ipsw/api/types"
|
|
"github.com/blacktop/ipsw/internal/db"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// Config is the server config
|
|
type Config struct {
|
|
Host string
|
|
Port int
|
|
Socket string
|
|
Debug bool
|
|
LogFile string
|
|
PemDB string
|
|
SigsDir string
|
|
}
|
|
|
|
// Server is the main server struct
|
|
type Server struct {
|
|
router *gin.Engine
|
|
server *http.Server
|
|
conf *Config
|
|
}
|
|
|
|
// NewServer creates a new server
|
|
func NewServer(conf *Config) *Server {
|
|
return &Server{
|
|
router: gin.Default(),
|
|
conf: conf,
|
|
}
|
|
}
|
|
|
|
// Start starts the server
|
|
func (s *Server) Start(db db.Database) error {
|
|
if s.conf.Debug {
|
|
log.SetLevel(log.DebugLevel)
|
|
}
|
|
|
|
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
|
defer stop()
|
|
|
|
if len(s.conf.LogFile) > 0 {
|
|
f, err := os.Create(s.conf.LogFile)
|
|
if err != nil {
|
|
return fmt.Errorf("server: failed to create log file: %v", err)
|
|
}
|
|
gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
|
|
}
|
|
|
|
s.router.GET("/version", func(c *gin.Context) {
|
|
c.JSON(http.StatusOK, types.Version{
|
|
APIVersion: api.DefaultVersion,
|
|
OSType: runtime.GOOS,
|
|
BuilderVersion: types.BuildVersion,
|
|
})
|
|
})
|
|
|
|
rg := s.router.Group("/v" + api.DefaultVersion)
|
|
|
|
routes.Add(rg, s.conf.PemDB)
|
|
|
|
if db != nil {
|
|
syms.AddRoutes(rg, db, s.conf.PemDB, s.conf.SigsDir)
|
|
}
|
|
|
|
if s.conf.PemDB != "" {
|
|
aea.AddRoutes(rg, s.conf.PemDB)
|
|
}
|
|
|
|
s.server = &http.Server{
|
|
Addr: net.JoinHostPort(s.conf.Host, strconv.Itoa(s.conf.Port)),
|
|
Handler: s.router,
|
|
}
|
|
|
|
go func() {
|
|
if len(s.conf.Socket) > 0 {
|
|
l, err := net.Listen("unix", filepath.Clean(s.conf.Socket))
|
|
if err != nil {
|
|
log.Fatalf("server: failed to listen: %v\n", err)
|
|
}
|
|
if err := s.server.Serve(l); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("server: failed to serve: %v\n", err)
|
|
}
|
|
} else {
|
|
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("server: failed to listen and serve: %v\n", err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Listen for the interrupt signal.
|
|
<-ctx.Done()
|
|
|
|
// Restore default behavior on the interrupt signal and notify user of shutdown.
|
|
stop()
|
|
|
|
log.Warn("Shutting down gracefully: Press Ctrl+C again to force")
|
|
|
|
return s.Stop()
|
|
}
|
|
|
|
// Stop stops the server
|
|
func (s *Server) Stop() error {
|
|
// The context is used to inform the server it has 5 seconds to finish
|
|
// the request it is currently handling
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
if err := s.server.Shutdown(ctx); err != nil {
|
|
return fmt.Errorf("server forced to shutdown: %v", err)
|
|
}
|
|
|
|
log.Info("Server Exiting")
|
|
|
|
return nil
|
|
}
|