out-of-tree kernel {module, exploit} development tool
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

kernel.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. // Copyright 2018 Mikhail Klementev. All rights reserved.
  2. // Use of this source code is governed by a AGPLv3 license
  3. // (or later) that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "log"
  10. "os"
  11. "os/exec"
  12. "os/user"
  13. "regexp"
  14. "strings"
  15. "time"
  16. "github.com/jollheef/out-of-tree/config"
  17. "github.com/naoina/toml"
  18. )
  19. func kernelListHandler(kcfg config.KernelConfig) (err error) {
  20. if len(kcfg.Kernels) == 0 {
  21. return errors.New("No kernels found")
  22. }
  23. for _, k := range kcfg.Kernels {
  24. fmt.Println(k.DistroType, k.DistroRelease, k.KernelRelease)
  25. }
  26. return
  27. }
  28. func matchDebianHeadersPkg(container, mask string, generic bool) (
  29. pkgs []string, err error) {
  30. cmd := "apt-cache search linux-headers | cut -d ' ' -f 1"
  31. output, err := dockerRun(time.Minute, container, "/tmp", cmd)
  32. if err != nil {
  33. return
  34. }
  35. r, err := regexp.Compile("linux-headers-" + mask)
  36. if err != nil {
  37. return
  38. }
  39. kernels := r.FindAll([]byte(output), -1)
  40. for _, k := range kernels {
  41. pkg := string(k)
  42. if generic && !strings.HasSuffix(pkg, "generic") {
  43. continue
  44. }
  45. if pkg == "linux-headers-generic" {
  46. continue
  47. }
  48. pkgs = append(pkgs, pkg)
  49. }
  50. return
  51. }
  52. func dockerImagePath(sk config.KernelMask) (path string, err error) {
  53. usr, err := user.Current()
  54. if err != nil {
  55. return
  56. }
  57. path = usr.HomeDir + "/.out-of-tree/"
  58. path += sk.DistroType.String() + "/" + sk.DistroRelease
  59. return
  60. }
  61. func generateBaseDockerImage(sk config.KernelMask) (err error) {
  62. imagePath, err := dockerImagePath(sk)
  63. if err != nil {
  64. return
  65. }
  66. dockerPath := imagePath + "/Dockerfile"
  67. d := "# BASE\n"
  68. if exists(dockerPath) {
  69. log.Printf("Base image for %s:%s found",
  70. sk.DistroType.String(), sk.DistroRelease)
  71. return
  72. } else {
  73. log.Printf("Base image for %s:%s not found, start generating",
  74. sk.DistroType.String(), sk.DistroRelease)
  75. os.MkdirAll(imagePath, os.ModePerm)
  76. }
  77. d += fmt.Sprintf("FROM %s:%s\n",
  78. strings.ToLower(sk.DistroType.String()),
  79. sk.DistroRelease,
  80. )
  81. switch sk.DistroType {
  82. case config.Ubuntu:
  83. d += "ENV DEBIAN_FRONTEND=noninteractive\n"
  84. d += "RUN apt-get update\n"
  85. d += "RUN apt-get install -y build-essential libelf-dev\n"
  86. d += "RUN apt-get install -y wget git\n"
  87. default:
  88. s := fmt.Sprintf("%s not yet supported", sk.DistroType.String())
  89. err = errors.New(s)
  90. return
  91. }
  92. d += "# END BASE\n\n"
  93. err = ioutil.WriteFile(dockerPath, []byte(d), 0644)
  94. if err != nil {
  95. return
  96. }
  97. cmd := exec.Command("docker", "build", "-t", sk.DockerName(), imagePath)
  98. rawOutput, err := cmd.CombinedOutput()
  99. if err != nil {
  100. log.Printf("Base image for %s:%s generating error, see log",
  101. sk.DistroType.String(), sk.DistroRelease)
  102. log.Println(string(rawOutput))
  103. return
  104. }
  105. log.Printf("Base image for %s:%s generating success",
  106. sk.DistroType.String(), sk.DistroRelease)
  107. return
  108. }
  109. func dockerImageAppend(sk config.KernelMask, pkgname string) (err error) {
  110. imagePath, err := dockerImagePath(sk)
  111. if err != nil {
  112. return
  113. }
  114. raw, err := ioutil.ReadFile(imagePath + "/Dockerfile")
  115. if err != nil {
  116. return
  117. }
  118. if strings.Contains(string(raw), pkgname) {
  119. // already installed kernel
  120. log.Printf("kernel %s for %s:%s is already exists",
  121. pkgname, sk.DistroType.String(), sk.DistroRelease)
  122. return
  123. }
  124. imagepkg := strings.Replace(pkgname, "headers", "image", -1)
  125. log.Printf("Start adding kernel %s for %s:%s",
  126. imagepkg, sk.DistroType.String(), sk.DistroRelease)
  127. s := fmt.Sprintf("RUN apt-get install -y %s %s\n", imagepkg, pkgname)
  128. err = ioutil.WriteFile(imagePath+"/Dockerfile",
  129. append(raw, []byte(s)...), 0644)
  130. if err != nil {
  131. return
  132. }
  133. cmd := exec.Command("docker", "build", "-t", sk.DockerName(), imagePath)
  134. rawOutput, err := cmd.CombinedOutput()
  135. if err != nil {
  136. // Fallback to previous state
  137. werr := ioutil.WriteFile(imagePath+"/Dockerfile", raw, 0644)
  138. if werr != nil {
  139. return
  140. }
  141. log.Printf("Add kernel %s for %s:%s error, see log",
  142. pkgname, sk.DistroType.String(), sk.DistroRelease)
  143. log.Println(string(rawOutput))
  144. return
  145. }
  146. log.Printf("Add kernel %s for %s:%s success",
  147. pkgname, sk.DistroType.String(), sk.DistroRelease)
  148. return
  149. }
  150. func kickImage(name string) (err error) {
  151. cmd := exec.Command("docker", "run", name, "bash", "-c", "ls")
  152. _, err = cmd.CombinedOutput()
  153. return
  154. }
  155. func copyKernels(name string) (err error) {
  156. cmd := exec.Command("docker", "ps", "-a")
  157. rawOutput, err := cmd.CombinedOutput()
  158. if err != nil {
  159. log.Println(string(rawOutput))
  160. return
  161. }
  162. r, err := regexp.Compile(".*" + name)
  163. if err != nil {
  164. return
  165. }
  166. var containerID string
  167. what := r.FindAll(rawOutput, -1)
  168. for _, w := range what {
  169. containerID = strings.Fields(string(w))[0]
  170. break
  171. }
  172. usr, err := user.Current()
  173. if err != nil {
  174. return
  175. }
  176. target := usr.HomeDir + "/.out-of-tree/kernels/"
  177. if !exists(target) {
  178. os.MkdirAll(target, os.ModePerm)
  179. }
  180. cmd = exec.Command("docker", "cp", containerID+":/boot/.", target)
  181. rawOutput, err = cmd.CombinedOutput()
  182. if err != nil {
  183. log.Println(string(rawOutput))
  184. return
  185. }
  186. return
  187. }
  188. func genKernelPath(files []os.FileInfo, kname string) string {
  189. for _, file := range files {
  190. if strings.Contains(file.Name(), "vmlinuz") {
  191. if strings.Contains(file.Name(), kname) {
  192. return file.Name()
  193. }
  194. }
  195. }
  196. return "unknown"
  197. }
  198. func genInitrdPath(files []os.FileInfo, kname string) string {
  199. for _, file := range files {
  200. if strings.Contains(file.Name(), "initrd") {
  201. if strings.Contains(file.Name(), kname) {
  202. return file.Name()
  203. }
  204. }
  205. }
  206. return "unknown"
  207. }
  208. func genRootfsImage(d dockerImageInfo) string {
  209. usr, err := user.Current()
  210. if err != nil {
  211. return fmt.Sprintln(err)
  212. }
  213. imageFile := d.ContainerName + ".img"
  214. return usr.HomeDir + "/.out-of-tree/images/" + imageFile
  215. }
  216. type dockerImageInfo struct {
  217. ContainerName string
  218. DistroType config.DistroType
  219. DistroRelease string // 18.04/7.4.1708/9.1
  220. }
  221. func listDockerImages() (diis []dockerImageInfo, err error) {
  222. cmd := exec.Command("docker", "images")
  223. rawOutput, err := cmd.CombinedOutput()
  224. if err != nil {
  225. return
  226. }
  227. r, err := regexp.Compile("out_of_tree_.*")
  228. if err != nil {
  229. return
  230. }
  231. containers := r.FindAll(rawOutput, -1)
  232. for _, c := range containers {
  233. container := strings.Fields(string(c))[0]
  234. s := strings.Replace(container, "__", ".", -1)
  235. values := strings.Split(s, "_")
  236. distro, ver := values[3], values[4]
  237. dii := dockerImageInfo{
  238. ContainerName: container,
  239. DistroRelease: ver,
  240. }
  241. dii.DistroType, err = config.NewDistroType(distro)
  242. if err != nil {
  243. return
  244. }
  245. diis = append(diis, dii)
  246. }
  247. return
  248. }
  249. func updateKernelsCfg() (err error) {
  250. dockerImages, err := listDockerImages()
  251. if err != nil {
  252. return
  253. }
  254. newkcfg := config.KernelConfig{}
  255. for _, d := range dockerImages {
  256. err = genKernels(d, &newkcfg)
  257. if err != nil {
  258. log.Println("gen kernels", d.ContainerName, ":", err)
  259. continue
  260. }
  261. }
  262. stripkcfg := config.KernelConfig{}
  263. for _, nk := range newkcfg.Kernels {
  264. if !hasKernel(nk, stripkcfg) {
  265. stripkcfg.Kernels = append(stripkcfg.Kernels, nk)
  266. }
  267. }
  268. buf, err := toml.Marshal(&stripkcfg)
  269. if err != nil {
  270. return
  271. }
  272. buf = append([]byte("# Autogenerated\n# DO NOT EDIT\n\n"), buf...)
  273. usr, err := user.Current()
  274. if err != nil {
  275. return
  276. }
  277. // TODO move all cfg path values to one provider
  278. kernelsCfgPath := usr.HomeDir + "/.out-of-tree/kernels.toml"
  279. err = ioutil.WriteFile(kernelsCfgPath, buf, 0644)
  280. if err != nil {
  281. return
  282. }
  283. log.Println(kernelsCfgPath, "is successfully updated")
  284. return
  285. }
  286. func genKernels(dii dockerImageInfo, newkcfg *config.KernelConfig) (
  287. err error) {
  288. name := dii.ContainerName
  289. cmd := exec.Command("docker", "run", name, "ls", "/lib/modules")
  290. rawOutput, err := cmd.CombinedOutput()
  291. if err != nil {
  292. log.Println(string(rawOutput), err)
  293. return
  294. }
  295. usr, err := user.Current()
  296. if err != nil {
  297. return
  298. }
  299. kernelsBase := usr.HomeDir + "/.out-of-tree/kernels/"
  300. files, err := ioutil.ReadDir(kernelsBase)
  301. if err != nil {
  302. return
  303. }
  304. for _, k := range strings.Fields(string(rawOutput)) {
  305. ki := config.KernelInfo{
  306. DistroType: dii.DistroType,
  307. DistroRelease: dii.DistroRelease,
  308. KernelRelease: k,
  309. ContainerName: name,
  310. KernelPath: kernelsBase + genKernelPath(files, k),
  311. InitrdPath: kernelsBase + genInitrdPath(files, k),
  312. RootFS: genRootfsImage(dii),
  313. }
  314. newkcfg.Kernels = append(newkcfg.Kernels, ki)
  315. }
  316. return
  317. }
  318. func hasKernel(ki config.KernelInfo, kcfg config.KernelConfig) bool {
  319. for _, sk := range kcfg.Kernels {
  320. if sk == ki {
  321. return true
  322. }
  323. }
  324. return false
  325. }
  326. func generateKernels(km config.KernelMask) (err error) {
  327. err = generateBaseDockerImage(km)
  328. if err != nil {
  329. return
  330. }
  331. var pkgs []string
  332. pkgs, err = matchDebianHeadersPkg(km.DockerName(),
  333. km.ReleaseMask, true)
  334. if err != nil {
  335. return
  336. }
  337. for i, pkg := range pkgs {
  338. log.Println(i, "/", len(pkgs))
  339. dockerImageAppend(km, pkg)
  340. }
  341. err = kickImage(km.DockerName())
  342. if err != nil {
  343. log.Println("kick image", km.DockerName(), ":", err)
  344. return
  345. }
  346. err = copyKernels(km.DockerName())
  347. if err != nil {
  348. log.Println("copy kernels", km.DockerName(), ":", err)
  349. return
  350. }
  351. return
  352. }
  353. func kernelAutogenHandler(workPath string) (err error) {
  354. ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
  355. if err != nil {
  356. return
  357. }
  358. for _, sk := range ka.SupportedKernels {
  359. if sk.DistroRelease == "" {
  360. err = errors.New("Please set distro_release")
  361. return
  362. }
  363. err = generateKernels(sk)
  364. if err != nil {
  365. return
  366. }
  367. }
  368. err = updateKernelsCfg()
  369. return
  370. }
  371. func kernelDockerRegenHandler() (err error) {
  372. dockerImages, err := listDockerImages()
  373. if err != nil {
  374. return
  375. }
  376. for _, d := range dockerImages {
  377. var imagePath string
  378. imagePath, err = dockerImagePath(config.KernelMask{
  379. DistroType: d.DistroType,
  380. DistroRelease: d.DistroRelease,
  381. })
  382. if err != nil {
  383. return
  384. }
  385. cmd := exec.Command("docker", "build", "-t",
  386. d.ContainerName, imagePath)
  387. var rawOutput []byte
  388. rawOutput, err = cmd.CombinedOutput()
  389. if err != nil {
  390. log.Println("docker build:", string(rawOutput))
  391. return
  392. }
  393. err = kickImage(d.ContainerName)
  394. if err != nil {
  395. log.Println("kick image", d.ContainerName, ":", err)
  396. continue
  397. }
  398. err = copyKernels(d.ContainerName)
  399. if err != nil {
  400. log.Println("copy kernels", d.ContainerName, ":", err)
  401. continue
  402. }
  403. }
  404. return updateKernelsCfg()
  405. }
  406. func kernelGenallHandler(distro, version string) (err error) {
  407. distroType, err := config.NewDistroType(distro)
  408. if err != nil {
  409. return
  410. }
  411. km := config.KernelMask{
  412. DistroType: distroType,
  413. DistroRelease: version,
  414. ReleaseMask: ".*",
  415. }
  416. err = generateKernels(km)
  417. if err != nil {
  418. return
  419. }
  420. return updateKernelsCfg()
  421. }