1
2
3
4
5
96
97 package ld
98
99 import (
100 "bufio"
101 "bytes"
102 "cmd/internal/obj"
103 "cmd/internal/objabi"
104 "cmd/link/internal/loader"
105 "cmd/link/internal/sym"
106 "crypto/hmac"
107 "crypto/sha256"
108 "debug/elf"
109 "debug/macho"
110 "debug/pe"
111 "encoding/binary"
112 "fmt"
113 "hash"
114 "io"
115 "os"
116 )
117
118 const enableFIPS = true
119
120
121 var fipsSyms = []struct {
122 name string
123 kind sym.SymKind
124 sym loader.Sym
125 seg *sym.Segment
126 }{
127 {name: "go:textfipsstart", kind: sym.STEXTFIPSSTART, seg: &Segtext},
128 {name: "go:textfipsend", kind: sym.STEXTFIPSEND},
129 {name: "go:rodatafipsstart", kind: sym.SRODATAFIPSSTART, seg: &Segrodata},
130 {name: "go:rodatafipsend", kind: sym.SRODATAFIPSEND},
131 {name: "go:noptrdatafipsstart", kind: sym.SNOPTRDATAFIPSSTART, seg: &Segdata},
132 {name: "go:noptrdatafipsend", kind: sym.SNOPTRDATAFIPSEND},
133 {name: "go:datafipsstart", kind: sym.SDATAFIPSSTART, seg: &Segdata},
134 {name: "go:datafipsend", kind: sym.SDATAFIPSEND},
135 }
136
137
138 var fipsinfo loader.Sym
139
140 const (
141 fipsMagic = "\xff Go fipsinfo \xff\x00"
142 fipsMagicLen = 16
143 fipsSumLen = 32
144 )
145
146
147 func loadfips(ctxt *Link) {
148 if !obj.EnableFIPS() {
149 return
150 }
151 if ctxt.BuildMode == BuildModePlugin {
152 return
153 }
154
155 ldr := ctxt.loader
156
157 info := ldr.CreateSymForUpdate("go:fipsinfo", 0)
158 info.SetType(sym.SFIPSINFO)
159
160 data := make([]byte, fipsMagicLen+fipsSumLen)
161 copy(data, fipsMagic)
162 info.SetData(data)
163 info.SetSize(int64(len(data)))
164 info.AddAddr(ctxt.Arch, info.Sym())
165
166 for i := range fipsSyms {
167 s := &fipsSyms[i]
168 sb := ldr.CreateSymForUpdate(s.name, 0)
169 sb.SetType(s.kind)
170 sb.SetLocal(true)
171 sb.SetSize(1)
172 s.sym = sb.Sym()
173 info.AddAddr(ctxt.Arch, s.sym)
174 if s.kind == sym.STEXTFIPSSTART || s.kind == sym.STEXTFIPSEND {
175 ctxt.Textp = append(ctxt.Textp, s.sym)
176 }
177 }
178
179 fipsinfo = info.Sym()
180 }
181
182
183
184 type fipsObj struct {
185 r io.ReaderAt
186 w io.Writer
187 wf *os.File
188 h hash.Hash
189 tmp [8]byte
190 }
191
192
193
194
195 func newFipsObj(r io.ReaderAt, fipso string) (*fipsObj, error) {
196 f := &fipsObj{r: r}
197 f.h = hmac.New(sha256.New, make([]byte, 32))
198 f.w = f.h
199 if fipso != "" {
200 wf, err := os.Create(fipso)
201 if err != nil {
202 return nil, err
203 }
204 f.wf = wf
205 f.w = io.MultiWriter(f.h, wf)
206 }
207
208 if _, err := f.w.Write([]byte("go fips object v1\n")); err != nil {
209 f.Close()
210 return nil, err
211 }
212 return f, nil
213 }
214
215
216
217
218 func (f *fipsObj) addSection(start, end int64) error {
219 n := end - start
220 binary.BigEndian.PutUint64(f.tmp[:], uint64(n))
221 f.w.Write(f.tmp[:])
222 _, err := io.Copy(f.w, io.NewSectionReader(f.r, start, n))
223 return err
224 }
225
226
227 func (f *fipsObj) sum() []byte {
228 return f.h.Sum(nil)
229 }
230
231
232
233 func (f *fipsObj) Close() error {
234 if f.wf != nil {
235 return f.wf.Close()
236 }
237 return nil
238 }
239
240
241
242
243 func asmbfips(ctxt *Link, fipso string) {
244 if !obj.EnableFIPS() {
245 return
246 }
247 if ctxt.LinkMode == LinkExternal {
248 return
249 }
250 if ctxt.BuildMode == BuildModePlugin {
251 return
252 }
253
254
255 f, err := newFipsObj(bytes.NewReader(ctxt.Out.Data()), fipso)
256 if err != nil {
257 Errorf("asmbfips: %v", err)
258 return
259 }
260 defer f.Close()
261
262
263 ldr := ctxt.loader
264 for i := 0; i < len(fipsSyms); i += 2 {
265 start := &fipsSyms[i]
266 end := &fipsSyms[i+1]
267 startAddr := ldr.SymValue(start.sym)
268 endAddr := ldr.SymValue(end.sym)
269 seg := start.seg
270 if seg.Vaddr == 0 && seg == &Segrodata {
271 seg = &Segtext
272 }
273 base := int64(seg.Fileoff - seg.Vaddr)
274 if !(seg.Vaddr <= uint64(startAddr) && startAddr <= endAddr && uint64(endAddr) <= seg.Vaddr+seg.Filelen) {
275 Errorf("asmbfips: %s not in expected segment (%#x..%#x not in %#x..%#x)", start.name, startAddr, endAddr, seg.Vaddr, seg.Vaddr+seg.Filelen)
276 return
277 }
278
279 if err := f.addSection(startAddr+base, endAddr+base); err != nil {
280 Errorf("asmbfips: %v", err)
281 return
282 }
283 }
284
285
286 addr := uint64(ldr.SymValue(fipsinfo))
287 seg := &Segdata
288 if !(seg.Vaddr <= addr && addr+32 < seg.Vaddr+seg.Filelen) {
289 Errorf("asmbfips: fipsinfo not in expected segment (%#x..%#x not in %#x..%#x)", addr, addr+32, seg.Vaddr, seg.Vaddr+seg.Filelen)
290 return
291 }
292 ctxt.Out.SeekSet(int64(seg.Fileoff + addr - seg.Vaddr + fipsMagicLen))
293 ctxt.Out.Write(f.sum())
294
295 if err := f.Close(); err != nil {
296 Errorf("asmbfips: %v", err)
297 return
298 }
299 }
300
301
302
303
304 func hostlinkfips(ctxt *Link, exe, fipso string) error {
305 if !obj.EnableFIPS() {
306 return nil
307 }
308 if ctxt.BuildMode == BuildModePlugin {
309 return nil
310 }
311 switch {
312 case ctxt.IsElf():
313 return elffips(ctxt, exe, fipso)
314 case ctxt.HeadType == objabi.Hdarwin:
315 return machofips(ctxt, exe, fipso)
316 case ctxt.HeadType == objabi.Hwindows:
317 return pefips(ctxt, exe, fipso)
318 }
319
320
321
322
323 return fmt.Errorf("fips unsupported on %s", ctxt.HeadType)
324 }
325
326
327
328 func machofips(ctxt *Link, exe, fipso string) error {
329
330 mf, err := macho.Open(exe)
331 if err != nil {
332 return err
333 }
334 defer mf.Close()
335
336 wf, err := os.OpenFile(exe, os.O_RDWR, 0)
337 if err != nil {
338 return err
339 }
340 defer wf.Close()
341
342 f, err := newFipsObj(wf, fipso)
343 if err != nil {
344 return err
345 }
346 defer f.Close()
347
348
349 sect := mf.Section("__go_fipsinfo")
350 if sect == nil {
351 return fmt.Errorf("cannot find __go_fipsinfo")
352 }
353 data, err := sect.Data()
354 if err != nil {
355 return err
356 }
357
358 uptr := ctxt.Arch.ByteOrder.Uint64
359 if ctxt.Arch.PtrSize == 4 {
360 uptr = func(x []byte) uint64 {
361 return uint64(ctxt.Arch.ByteOrder.Uint32(x))
362 }
363 }
364
365
366
367
368
369
370
371
372
373
374
375
376 const addendMask = 1<<48 - 1
377 data = data[fipsMagicLen+fipsSumLen:]
378 self := int64(uptr(data)) & addendMask
379 base := int64(sect.Offset) - self
380 data = data[ctxt.Arch.PtrSize:]
381
382 for i := 0; i < 4; i++ {
383 start := int64(uptr(data[0:]))&addendMask + base
384 end := int64(uptr(data[ctxt.Arch.PtrSize:]))&addendMask + base
385 data = data[2*ctxt.Arch.PtrSize:]
386 if err := f.addSection(start, end); err != nil {
387 return err
388 }
389 }
390
391
392 if _, err := wf.WriteAt(f.sum(), int64(sect.Offset)+fipsMagicLen); err != nil {
393 return err
394 }
395 if err := wf.Close(); err != nil {
396 return err
397 }
398 return f.Close()
399 }
400
401
402
403 func elffips(ctxt *Link, exe, fipso string) error {
404
405 ef, err := elf.Open(exe)
406 if err != nil {
407 return err
408 }
409 defer ef.Close()
410
411 wf, err := os.OpenFile(exe, os.O_RDWR, 0)
412 if err != nil {
413 return err
414 }
415 defer wf.Close()
416
417 f, err := newFipsObj(wf, fipso)
418 if err != nil {
419 return err
420 }
421 defer f.Close()
422
423
424 sect := ef.Section(".go.fipsinfo")
425 if sect == nil {
426 return fmt.Errorf("cannot find .go.fipsinfo")
427 }
428
429 data, err := sect.Data()
430 if err != nil {
431 return err
432 }
433
434 uptr := ctxt.Arch.ByteOrder.Uint64
435 if ctxt.Arch.PtrSize == 4 {
436 uptr = func(x []byte) uint64 {
437 return uint64(ctxt.Arch.ByteOrder.Uint32(x))
438 }
439 }
440
441
442
443
444
445
446
447
448
449
450 data = data[fipsMagicLen+fipsSumLen:]
451 data = data[ctxt.Arch.PtrSize:]
452
453 Addrs:
454 for i := 0; i < 4; i++ {
455 start := uptr(data[0:])
456 end := uptr(data[ctxt.Arch.PtrSize:])
457 data = data[2*ctxt.Arch.PtrSize:]
458 for _, prog := range ef.Progs {
459 if prog.Type == elf.PT_LOAD && prog.Vaddr <= start && start <= end && end <= prog.Vaddr+prog.Filesz {
460 if err := f.addSection(int64(start+prog.Off-prog.Vaddr), int64(end+prog.Off-prog.Vaddr)); err != nil {
461 return err
462 }
463 continue Addrs
464 }
465 }
466 return fmt.Errorf("invalid pointers found in .go.fipsinfo")
467 }
468
469
470 if _, err := wf.WriteAt(f.sum(), int64(sect.Offset)+fipsMagicLen); err != nil {
471 return err
472 }
473 if err := wf.Close(); err != nil {
474 return err
475 }
476 return f.Close()
477 }
478
479
480
481 func pefips(ctxt *Link, exe, fipso string) error {
482
483 pf, err := pe.Open(exe)
484 if err != nil {
485 return err
486 }
487 defer pf.Close()
488
489 wf, err := os.OpenFile(exe, os.O_RDWR, 0)
490 if err != nil {
491 return err
492 }
493 defer wf.Close()
494
495 f, err := newFipsObj(wf, fipso)
496 if err != nil {
497 return err
498 }
499 defer f.Close()
500
501
502
503
504
505 const maxScan = 16 << 20
506 sect := pf.Section(".data")
507 if sect == nil {
508 return fmt.Errorf("cannot find .data")
509 }
510 b := bufio.NewReader(sect.Open())
511 off := int64(0)
512 data := make([]byte, fipsMagicLen+fipsSumLen+9*ctxt.Arch.PtrSize)
513 for ; ; off += 16 {
514 if off >= maxScan {
515 break
516 }
517 if _, err := io.ReadFull(b, data[:fipsMagicLen]); err != nil {
518 return fmt.Errorf("scanning PE for FIPS magic: %v", err)
519 }
520 if string(data[:fipsMagicLen]) == fipsMagic {
521 if _, err := io.ReadFull(b, data[fipsMagicLen:]); err != nil {
522 return fmt.Errorf("scanning PE for FIPS magic: %v", err)
523 }
524 break
525 }
526 }
527
528 uptr := ctxt.Arch.ByteOrder.Uint64
529 if ctxt.Arch.PtrSize == 4 {
530 uptr = func(x []byte) uint64 {
531 return uint64(ctxt.Arch.ByteOrder.Uint32(x))
532 }
533 }
534
535
536
537
538
539
540
541
542 data = data[fipsMagicLen+fipsSumLen:]
543 self := int64(uptr(data))
544 data = data[ctxt.Arch.PtrSize:]
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568 peself := int64(sect.VirtualAddress) + off
569 if self&0xfff != off&0xfff {
570 return fmt.Errorf("corrupt pointer found in go:fipsinfo")
571 }
572 delta := peself - self
573
574 Addrs:
575 for i := 0; i < 4; i++ {
576 start := int64(uptr(data[0:])) + delta
577 end := int64(uptr(data[ctxt.Arch.PtrSize:])) + delta
578 data = data[2*ctxt.Arch.PtrSize:]
579 for _, sect := range pf.Sections {
580 if int64(sect.VirtualAddress) <= start && start <= end && end <= int64(sect.VirtualAddress)+int64(sect.Size) {
581 off := int64(sect.Offset) - int64(sect.VirtualAddress)
582 if err := f.addSection(start+off, end+off); err != nil {
583 return err
584 }
585 continue Addrs
586 }
587 }
588 return fmt.Errorf("invalid pointers found in go:fipsinfo")
589 }
590
591
592 if _, err := wf.WriteAt(f.sum(), int64(sect.Offset)+off+fipsMagicLen); err != nil {
593 return err
594 }
595 if err := wf.Close(); err != nil {
596 return err
597 }
598 return f.Close()
599 }
600
View as plain text