diff --git a/token-validator/challenge.go b/token-validator/challenge.go index e3ed15f..dcec5b9 100644 --- a/token-validator/challenge.go +++ b/token-validator/challenge.go @@ -112,6 +112,31 @@ func challengeTime(s *Student, t *givenToken, chid int) error { } } +func challengePing(s *Student, t *givenToken, chid int) error { + var expected []byte + switch s.Id % 5 { + case 1: + expected = []byte("baaaaaad") + case 2: + expected = []byte("baadfood") + case 3: + expected = []byte("baddcafe") + case 4: + expected = []byte("cafebabe") + default: + expected = []byte("deadbeef") + } + + pkey := s.GetPKey() + if expectedToken, err := GenerateToken(pkey, chid, expected); err != nil { + return err + } else if ! hmac.Equal(expectedToken, t.token) { + return errors.New("This is not the expected token.") + } else { + return nil + } +} + func challengeDisk(s *Student, t *givenToken, chid int) error { pkey := fmt.Sprintf("%x", s.GetPKey()) @@ -164,28 +189,37 @@ func init() { Check: challengeTime, }, - /* Challenge 4 : disk */ - Challenge{ - Accessible: []func(*Student, *http.Request) error{noAccessRestriction}, - Check: challengeDisk, - }, - - /* Challenge 5 : DNS TXT */ + /* Challenge 4 : DNS TXT */ Challenge{ Accessible: []func(*Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly}, Check: challengeDNS, }, - /* Challenge 6 : time net */ + /* Challenge 5 : time net */ Challenge{ Accessible: []func(*Student, *http.Request) error{maxProxy(1)}, Check: challengeTime, }, + + /* Bonus 1 : echo request */ + Challenge{ + Accessible: []func(*Student, *http.Request) error{noAccessRestriction}, + Check: challengePing, + }, + + /* Bonus 2 : disk */ + Challenge{ + Accessible: []func(*Student, *http.Request) error{noAccessRestriction}, + Check: challengeDisk, + }, + } router.GET("/challenge", apiHandler(getChallengeList)) router.GET("/challenge/:chid", rawHandler(accessibleChallenge)) - router.POST("/challenge", rawHandler(receiveToken)) + router.POST("/challenge", rawHandler(challengeHandler(receiveToken))) + router.POST("/echorequest", rawHandler(definedChallengeHandler(receiveToken, 6))) + router.POST("/testdisk", rawHandler(definedChallengeHandler(receiveToken, 7))) } type givenToken struct { @@ -219,7 +253,7 @@ func accessibleChallenge(r *http.Request, ps httprouter.Params, _ []byte) (inter } } -func receiveToken(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) { +func receiveToken(r *http.Request, body []byte, chid int) (interface{}, error) { var gt givenToken if err := json.Unmarshal(body, >); err != nil { return nil, err @@ -231,22 +265,11 @@ func receiveToken(r *http.Request, ps httprouter.Params, body []byte) (interface } // Find challenge ID - var chid int - var err error - if chid, err = strconv.Atoi(string(ps.ByName("chid"))); err != nil { - if gt.Challenge > 0 { - chid = gt.Challenge - } else if string(ps.ByName("chid")) != "" { - return nil, err - } - err = nil + if gt.Challenge >= 1 { + chid = gt.Challenge } - if chid == 0 { - chid = 4 - } - - if chid > len(challenges) { + if chid <= 0 || chid - 1 > len(challenges) { return nil, errors.New("This challenge doesn't exist") } diff --git a/token-validator/handler.go b/token-validator/handler.go index e4edab0..1fa7b32 100644 --- a/token-validator/handler.go +++ b/token-validator/handler.go @@ -107,6 +107,18 @@ func rawHandler(f func(*http.Request, httprouter.Params, []byte) (interface{}, e } } +func challengeHandler(f func (*http.Request, []byte, int) (interface{}, error)) func(*http.Request, httprouter.Params, []byte) (interface{}, error) { + return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) { + return f(r, body, 0) + } +} + +func definedChallengeHandler(f func (*http.Request, []byte, int) (interface{}, error), chid int) func(*http.Request, httprouter.Params, []byte) (interface{}, error) { + return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) { + return f(r, body, chid) + } +} + func apiHandler(f DispatchFunction, access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) { return rawHandler(func (_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }, access...) } diff --git a/token-validator/ip.go b/token-validator/ip.go index aa058b7..598e376 100644 --- a/token-validator/ip.go +++ b/token-validator/ip.go @@ -24,10 +24,13 @@ func showIPs(_ httprouter.Params, body []byte) (interface{}, error) { } for _, std := range students { - r[std.Login] = make(map[string]string) + sid := fmt.Sprintf("%d", std.Id) - r[std.Login]["vlan0"] = fmt.Sprintf("172.23.0.%d", std.IPSuffix()) - r[std.Login]["vlan7"] = fmt.Sprintf("172.23.142.%d", std.IPSuffix()) + r[sid] = make(map[string]string) + + r[sid]["mac"] = std.MAC + r[sid]["vlan0"] = fmt.Sprintf("172.23.0.%d", std.IPSuffix()) + r[sid]["vlan7"] = fmt.Sprintf("172.23.142.%d", std.IPSuffix()) } return r, nil diff --git a/token-validator/ping.go b/token-validator/ping.go index 3c6fea1..5b096ab 100644 --- a/token-validator/ping.go +++ b/token-validator/ping.go @@ -1,14 +1,63 @@ package main import ( + "encoding/json" + "errors" "time" ) +var PongSecret = "felixfixit" + func init() { + router.GET("/api/students/:sid/ping", apiHandler(studentHandler(isPinging))) + router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func (student Student, body []byte) (interface{}, error) { + return student.lastPongs() + }))) router.POST("/api/students/:sid/pong", apiHandler(studentHandler(stdPong), sslOnly)) } +func isPinging(student Student, body []byte) (interface{}, error) { + if pongs, err := student.lastPongs(); err != nil { + return nil, err + } else if len(pongs) <= 0 { + return false, nil + } else { + return time.Now().Before(pongs[0].Add(time.Second * 10)), nil + } +} + +func (s Student) lastPongs() (pongs []time.Time, err error) { + if rows, errr := DBQuery("SELECT time FROM student_pong WHERE id_student = ? ORDER BY time DESC", s.Id); errr != nil { + return nil, errr + } else { + defer rows.Close() + + for rows.Next() { + var t time.Time + if err = rows.Scan(&t); err != nil { + return + } + pongs = append(pongs, t) + } + if err = rows.Err(); err != nil { + return + } + + return + } +} + func stdPong(student Student, body []byte) (interface{}, error) { + var gt givenToken + if err := json.Unmarshal(body, >); err != nil { + return nil, err + } + + if gt.Token != PongSecret { + return nil, errors.New("This is not the expected token.") + } + + if res, err := DBExec("INSERT INTO student_pong (id_student, time) VALUES (?, ?)", student.Id, time.Now()); err != nil { return false, err } else if _, err := res.LastInsertId(); err != nil { diff --git a/token-validator/students.go b/token-validator/students.go index 45218a1..4ad3e4d 100644 --- a/token-validator/students.go +++ b/token-validator/students.go @@ -87,7 +87,7 @@ func getStudent(id int) (s Student, err error) { } func getStudentByLogin(login string) (s Student, err error) { - err = DBQueryRow("SELECT id_student, login, time FROM students WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time) + err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC) return } @@ -155,6 +155,9 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) { } } exist.registerAccess(std.IP, std.MAC) + + exist.IP = fmt.Sprintf("172.23.0.%d", exist.IPSuffix()) + return exist, nil }