aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric M. Ludlam2012-01-09 14:12:11 +0800
committerChong Yidong2012-01-09 14:12:11 +0800
commita62d5ee188dcb532088a15b0a2f066d3305b2eda (patch)
tree574682d524389c5b21730c379edf818f852d69e2
parent866b58d61afd2eccfed53d1707bea233bde3c030 (diff)
downloademacs-a62d5ee188dcb532088a15b0a2f066d3305b2eda.tar.gz
emacs-a62d5ee188dcb532088a15b0a2f066d3305b2eda.zip
Fix EDE security flaw involving loading arbitrary Lisp from Project.ede.
* lisp/ede.el (ede-project-directories): New option. (ede-directory-safe-p): Check it. (ede-initialize-state-current-buffer, ede, ede-new) (ede-check-project-directory, ede-rescan-toplevel) (ede-load-project-file, ede-parent-project, ede-current-project): (ede-target-parent): Avoid loading in a project unless it is safe, since it may involve malicious code. This security flaw was pointed out by Hiroshi Oota. * lisp/ede/auto.el (ede-project-autoload): Add safe-p slot. (ede-project-class-files): Projects using Project.ede are unsafe. (ede-auto-load-project): New method. * lisp/ede/simple.el (ede-project-class-files): Mark as unsafe.
-rw-r--r--lisp/cedet/ChangeLog17
-rw-r--r--lisp/cedet/ede.el215
-rw-r--r--lisp/cedet/ede/auto.el28
-rw-r--r--lisp/cedet/ede/simple.el3
4 files changed, 211 insertions, 52 deletions
diff --git a/lisp/cedet/ChangeLog b/lisp/cedet/ChangeLog
index d8a4209cc98..dc43253fcd7 100644
--- a/lisp/cedet/ChangeLog
+++ b/lisp/cedet/ChangeLog
@@ -1,3 +1,20 @@
12012-01-09 Eric Ludlam <zappo@gnu.org>
2
3 * ede.el (ede-project-directories): New option.
4 (ede-directory-safe-p): Check it.
5 (ede-initialize-state-current-buffer, ede, ede-new)
6 (ede-check-project-directory, ede-rescan-toplevel)
7 (ede-load-project-file, ede-parent-project, ede-current-project):
8 (ede-target-parent): Avoid loading in a project unless it is safe,
9 since it may involve malicious code. This security flaw was
10 pointed out by Hiroshi Oota.
11
12 * ede/auto.el (ede-project-autoload): Add safe-p slot.
13 (ede-project-class-files): Projects using Project.ede are unsafe.
14 (ede-auto-load-project): New method.
15
16 * ede/simple.el (ede-project-class-files): Mark as unsafe.
17
12011-04-13 Juanma Barranquero <lekktu@gmail.com> 182011-04-13 Juanma Barranquero <lekktu@gmail.com>
2 19
3 * ede/pconf.el (ede-proj-tweak-autoconf, ede-proj-flush-autoconf): 20 * ede/pconf.el (ede-proj-tweak-autoconf, ede-proj-flush-autoconf):
diff --git a/lisp/cedet/ede.el b/lisp/cedet/ede.el
index f0450d539a3..c3a223fad80 100644
--- a/lisp/cedet/ede.el
+++ b/lisp/cedet/ede.el
@@ -94,6 +94,42 @@ target willing to take the file. 'never means never perform the check."
94 :group 'ede 94 :group 'ede
95 :type 'sexp) ; make this be a list of options some day 95 :type 'sexp) ; make this be a list of options some day
96 96
97(defcustom ede-project-directories nil
98 "Directories in which EDE may search for project files.
99If the value is t, EDE may search in any directory.
100
101If the value is a function, EDE calls that function with one
102argument, the directory name; the function should return t iff
103EDE should look for project files in the directory.
104
105Otherwise, the value should be a list of fully-expanded directory
106names. EDE searches for project files only in those directories.
107If you invoke the commands \\[ede] or \\[ede-new] on a directory
108that is not listed, Emacs will offer to add it to the list.
109
110Any other value disables searching for EDE project files."
111 :group 'ede
112 :type '(choice (const :tag "Any directory" t)
113 (repeat :tag "List of directories"
114 (directory))
115 (function :tag "Predicate"))
116 :version "23.4"
117 :risky t)
118
119(defun ede-directory-safe-p (dir)
120 "Return non-nil if DIR is a safe directory to load projects from.
121Projects that do not load a project definition as Emacs Lisp code
122are safe, and can be loaded automatically. Other project types,
123such as those created with Project.ede files, are safe only if
124specified by `ede-project-directories'."
125 (setq dir (directory-file-name (expand-file-name dir)))
126 ;; Load only if allowed by `ede-project-directories'.
127 (or (eq ede-project-directories t)
128 (and (functionp ede-project-directories)
129 (funcall ede-project-directories dir))
130 (and (listp ede-project-directories)
131 (member dir ede-project-directories))))
132
97 133
98;;; Management variables 134;;; Management variables
99 135
@@ -419,24 +455,42 @@ provided `global-ede-mode' is enabled."
419Sets buffer local variables for EDE." 455Sets buffer local variables for EDE."
420 (let* ((ROOT nil) 456 (let* ((ROOT nil)
421 (proj (ede-directory-get-open-project default-directory 457 (proj (ede-directory-get-open-project default-directory
422 'ROOT))) 458 'ROOT))
459 (projauto nil))
460
423 (when (or proj ROOT 461 (when (or proj ROOT
424 (ede-directory-project-p default-directory t)) 462 ;; If there is no open project, look up the project
463 ;; autoloader to see if we should initialize.
464 (setq projauto (ede-directory-project-p default-directory t)))
465
466 (when (and (not proj) projauto)
467
468 ;; No project was loaded, but we have a project description
469 ;; object. This means that we can check if it is a safe
470 ;; project to load before requesting it to be loaded.
425 471
426 (when (not proj) 472 (when (or (oref projauto safe-p)
427 ;; @todo - this could be wasteful. 473 ;; The project style is not safe, so check if it is
428 (setq proj (ede-load-project-file default-directory 'ROOT))) 474 ;; in `ede-project-directories'.
475 (let ((top (ede-toplevel-project default-directory)))
476 (ede-directory-safe-p top)))
429 477
430 (setq ede-object (ede-buffer-object (current-buffer) 478 ;; The project is safe, so load it in.
479 (setq proj (ede-load-project-file default-directory 'ROOT))))
480
481 ;; Only initialize EDE state in this buffer if we found a project.
482 (when proj
483
484 (setq ede-object (ede-buffer-object (current-buffer)
431 'ede-object-project)) 485 'ede-object-project))
432 486
433 (setq ede-object-root-project 487 (setq ede-object-root-project
434 (or ROOT (ede-project-root ede-object-project))) 488 (or ROOT (ede-project-root ede-object-project)))
435 489
436 (if (and (not ede-object) ede-object-project) 490 (if (and (not ede-object) ede-object-project)
437 (ede-auto-add-to-target)) 491 (ede-auto-add-to-target))
438 492
439 (ede-apply-target-options)))) 493 (ede-apply-target-options)))))
440 494
441(defun ede-reset-all-buffers (onoff) 495(defun ede-reset-all-buffers (onoff)
442 "Reset all the buffers due to change in EDE. 496 "Reset all the buffers due to change in EDE.
@@ -555,13 +609,73 @@ of objects with the `ede-want-file-p' method."
555 609
556;;; Interactive method invocations 610;;; Interactive method invocations
557;; 611;;
558(defun ede (file) 612(defun ede (dir)
559 "Start up EDE on something. 613 "Start up EDE for directory DIR.
560Argument FILE is the file or directory to load a project from." 614If DIR has an existing project file, load it.
561 (interactive "fProject File: ") 615Otherwise, create a new project for DIR."
562 (if (not (file-exists-p file)) 616 (interactive
563 (ede-new file) 617 ;; When choosing a directory to turn on, and we see some directory here,
564 (ede-load-project-file (file-name-directory file)))) 618 ;; provide that as the default.
619 (let* ((top (ede-toplevel-project default-directory))
620 (promptdflt (or top default-directory)))
621 (list (read-directory-name "Project directory: "
622 promptdflt promptdflt t))))
623 (unless (file-directory-p dir)
624 (error "%s is not a directory" dir))
625 (when (ede-directory-get-open-project dir)
626 (error "%s already has an open project associated with it" dir))
627
628 ;; Check if the directory has been added to the list of safe
629 ;; directories. It can also add the directory to the safe list if
630 ;; the user chooses.
631 (if (ede-check-project-directory dir)
632 (progn
633 ;; If there is a project in DIR, load it, otherwise do
634 ;; nothing.
635 (ede-load-project-file dir)
636
637 ;; Check if we loaded anything on the previous line.
638 (if (ede-current-project dir)
639
640 ;; We successfully opened an existing project. Some open
641 ;; buffers may also be referring to this project.
642 ;; Resetting all the buffers will get them to also point
643 ;; at this new open project.
644 (ede-reset-all-buffers 1)
645
646 ;; ELSE
647 ;; There was no project, so switch to `ede-new' which is how
648 ;; a user can select a new kind of project to create.
649 (let ((default-directory (expand-file-name dir)))
650 (call-interactively 'ede-new))))
651
652 ;; If the proposed directory isn't safe, then say so.
653 (error "%s is not an allowed project directory in `ede-project-directories'"
654 dir)))
655
656(defun ede-check-project-directory (dir)
657 "Check if DIR should be in `ede-project-directories'.
658If it is not, try asking the user if it should be added; if so,
659add it and save `ede-project-directories' via Customize.
660Return nil iff DIR should not be in `ede-project-directories'."
661 (setq dir (directory-file-name (expand-file-name dir))) ; strip trailing /
662 (or (eq ede-project-directories t)
663 (and (functionp ede-project-directories)
664 (funcall ede-project-directories dir))
665 ;; If `ede-project-directories' is a list, maybe add it.
666 (when (listp ede-project-directories)
667 (or (member dir ede-project-directories)
668 (when (y-or-n-p (format "`%s' is not listed in `ede-project-directories'.
669Add it to the list of allowed project directories? "
670 dir))
671 (push dir ede-project-directories)
672 ;; If possible, save `ede-project-directories'.
673 (if (or custom-file user-init-file)
674 (let ((coding-system-for-read nil))
675 (customize-save-variable
676 'ede-project-directories
677 ede-project-directories)))
678 t)))))
565 679
566(defun ede-new (type &optional name) 680(defun ede-new (type &optional name)
567 "Create a new project starting of project type TYPE. 681 "Create a new project starting of project type TYPE.
@@ -596,6 +710,11 @@ Optional argument NAME is the name to give this project."
596 (error "Cannot create project in non-existent directory %s" default-directory)) 710 (error "Cannot create project in non-existent directory %s" default-directory))
597 (when (not (file-writable-p default-directory)) 711 (when (not (file-writable-p default-directory))
598 (error "No write permissions for %s" default-directory)) 712 (error "No write permissions for %s" default-directory))
713 (unless (ede-check-project-directory default-directory)
714 (error "%s is not an allowed project directory in `ede-project-directories'"
715 default-directory))
716 ;; Make sure the project directory is loadable in the future.
717 (ede-check-project-directory default-directory)
599 ;; Create the project 718 ;; Create the project
600 (let* ((obj (object-assoc type 'name ede-project-class-files)) 719 (let* ((obj (object-assoc type 'name ede-project-class-files))
601 (nobj (let ((f (oref obj file)) 720 (nobj (let ((f (oref obj file))
@@ -629,6 +748,10 @@ Optional argument NAME is the name to give this project."
629 (ede-add-subproject pp nobj) 748 (ede-add-subproject pp nobj)
630 (ede-commit-project pp))) 749 (ede-commit-project pp)))
631 (ede-commit-project nobj)) 750 (ede-commit-project nobj))
751 ;; Once the project is created, load it again. This used to happen
752 ;; lazily, but with project loading occurring less often and with
753 ;; security in mind, this is now the safe time to reload.
754 (ede-load-project-file default-directory)
632 ;; Have the menu appear 755 ;; Have the menu appear
633 (setq ede-minor-mode t) 756 (setq ede-minor-mode t)
634 ;; Allert the user 757 ;; Allert the user
@@ -651,11 +774,16 @@ ARGS are additional arguments to pass to method sym."
651(defun ede-rescan-toplevel () 774(defun ede-rescan-toplevel ()
652 "Rescan all project files." 775 "Rescan all project files."
653 (interactive) 776 (interactive)
654 (let ((toppath (ede-toplevel-project default-directory)) 777 (if (not (ede-directory-get-open-project default-directory))
655 (ede-deep-rescan t)) 778 ;; This directory isn't open. Can't rescan.
656 (project-rescan (ede-load-project-file toppath)) 779 (error "Attempt to rescan a project that isn't open")
657 (ede-reset-all-buffers 1) 780
658 )) 781 ;; Continue
782 (let ((toppath (ede-toplevel-project default-directory))
783 (ede-deep-rescan t))
784
785 (project-rescan (ede-load-project-file toppath))
786 (ede-reset-all-buffers 1))))
659 787
660(defun ede-new-target (&rest args) 788(defun ede-new-target (&rest args)
661 "Create a new target specific to this type of project file. 789 "Create a new target specific to this type of project file.
@@ -891,7 +1019,7 @@ Optional ROOTRETURN will return the root project for DIR."
891 ;; Do the load 1019 ;; Do the load
892 ;;(message "EDE LOAD : %S" file) 1020 ;;(message "EDE LOAD : %S" file)
893 (let* ((file dir) 1021 (let* ((file dir)
894 (path (expand-file-name (file-name-directory file))) 1022 (path (file-name-as-directory (expand-file-name dir)))
895 (pfc (ede-directory-project-p path)) 1023 (pfc (ede-directory-project-p path))
896 (toppath nil) 1024 (toppath nil)
897 (o nil)) 1025 (o nil))
@@ -920,13 +1048,11 @@ Optional ROOTRETURN will return the root project for DIR."
920 ;; See if it's been loaded before 1048 ;; See if it's been loaded before
921 (setq o (object-assoc (ede-dir-to-projectfile pfc toppath) 'file 1049 (setq o (object-assoc (ede-dir-to-projectfile pfc toppath) 'file
922 ede-projects)) 1050 ede-projects))
923 (if (not o) 1051
924 ;; If not, get it now. 1052 ;; If not open yet, load it.
925 (let ((ede-constructing pfc)) 1053 (unless o
926 (setq o (funcall (oref pfc load-type) toppath)) 1054 (let ((ede-constructing pfc))
927 (when (not o) 1055 (setq o (ede-auto-load-project pfc toppath))))
928 (error "Project type error: :load-type failed to create a project"))
929 (ede-add-project-to-global-list o)))
930 1056
931 ;; Return the found root project. 1057 ;; Return the found root project.
932 (when rootreturn (set rootreturn o)) 1058 (when rootreturn (set rootreturn o))
@@ -980,13 +1106,7 @@ Optional argument OBJ is an object to find the parent of."
980 (and root 1106 (and root
981 (ede-find-subproject-for-directory root updir)) 1107 (ede-find-subproject-for-directory root updir))
982 ;; Try the all structure based search. 1108 ;; Try the all structure based search.
983 (ede-directory-get-open-project updir) 1109 (ede-directory-get-open-project updir))))))))
984 ;; Load up the project file as a last resort.
985 ;; Last resort since it uses file-truename, and other
986 ;; slow features.
987 (and (ede-directory-project-p updir)
988 (ede-load-project-file
989 (file-name-as-directory updir))))))))))
990 1110
991(defun ede-current-project (&optional dir) 1111(defun ede-current-project (&optional dir)
992 "Return the current project file. 1112 "Return the current project file.
@@ -1000,11 +1120,7 @@ If optional DIR is provided, get the project for DIR instead."
1000 ;; No current project. 1120 ;; No current project.
1001 (when (not ans) 1121 (when (not ans)
1002 (let* ((ldir (or dir default-directory))) 1122 (let* ((ldir (or dir default-directory)))
1003 (setq ans (ede-directory-get-open-project ldir)) 1123 (setq ans (ede-directory-get-open-project ldir))))
1004 (or ans
1005 ;; No open project, if this dir pass project-p, then load.
1006 (when (ede-directory-project-p ldir)
1007 (setq ans (ede-load-project-file ldir))))))
1008 ;; Return what we found. 1124 ;; Return what we found.
1009 ans)) 1125 ans))
1010 1126
@@ -1059,12 +1175,13 @@ If TARGET belongs to a subproject, return that project file."
1059 "Return the project which is the parent of TARGET. 1175 "Return the project which is the parent of TARGET.
1060It is recommended you track the project a different way as this function 1176It is recommended you track the project a different way as this function
1061could become slow in time." 1177could become slow in time."
1062 ;; @todo - use ede-object-project as a starting point. 1178 (or ede-object-project
1063 (let ((ans nil) (projs ede-projects)) 1179 ;; If not cached, derive it from the current directory of the target.
1064 (while (and (not ans) projs) 1180 (let ((ans nil) (projs ede-projects))
1065 (setq ans (ede-target-in-project-p (car projs) target) 1181 (while (and (not ans) projs)
1066 projs (cdr projs))) 1182 (setq ans (ede-target-in-project-p (car projs) target)
1067 ans)) 1183 projs (cdr projs)))
1184 ans)))
1068 1185
1069(defmethod ede-find-target ((proj ede-project) buffer) 1186(defmethod ede-find-target ((proj ede-project) buffer)
1070 "Fetch the target in PROJ belonging to BUFFER or nil." 1187 "Fetch the target in PROJ belonging to BUFFER or nil."
diff --git a/lisp/cedet/ede/auto.el b/lisp/cedet/ede/auto.el
index 36f902d4e1c..0d84ee41d2c 100644
--- a/lisp/cedet/ede/auto.el
+++ b/lisp/cedet/ede/auto.el
@@ -58,6 +58,13 @@ associated with a single object class, based on the initilizeres used.")
58 :initform t 58 :initform t
59 :documentation 59 :documentation
60 "Non-nil if this is an option when a user creates a project.") 60 "Non-nil if this is an option when a user creates a project.")
61 (safe-p :initarg :safe-p
62 :initform t
63 :documentation
64 "Non-nil if the project load files are \"safe\".
65An unsafe project is one that loads project variables via Emacs
66Lisp code. A safe project is one that loads project variables by
67scanning files without loading Lisp code from them.")
61 ) 68 )
62 "Class representing minimal knowledge set to run preliminary EDE functions. 69 "Class representing minimal knowledge set to run preliminary EDE functions.
63When more advanced functionality is needed from a project type, that projects 70When more advanced functionality is needed from a project type, that projects
@@ -69,13 +76,15 @@ type is required and the load function used.")
69 :name "Make" :file 'ede/proj 76 :name "Make" :file 'ede/proj
70 :proj-file "Project.ede" 77 :proj-file "Project.ede"
71 :load-type 'ede-proj-load 78 :load-type 'ede-proj-load
72 :class-sym 'ede-proj-project) 79 :class-sym 'ede-proj-project
80 :safe-p nil)
73 (ede-project-autoload "edeproject-automake" 81 (ede-project-autoload "edeproject-automake"
74 :name "Automake" :file 'ede/proj 82 :name "Automake" :file 'ede/proj
75 :proj-file "Project.ede" 83 :proj-file "Project.ede"
76 :initializers '(:makefile-type Makefile.am) 84 :initializers '(:makefile-type Makefile.am)
77 :load-type 'ede-proj-load 85 :load-type 'ede-proj-load
78 :class-sym 'ede-proj-project) 86 :class-sym 'ede-proj-project
87 :safe-p nil)
79 (ede-project-autoload "automake" 88 (ede-project-autoload "automake"
80 :name "automake" :file 'ede/project-am 89 :name "automake" :file 'ede/project-am
81 :proj-file "Makefile.am" 90 :proj-file "Makefile.am"
@@ -84,6 +93,8 @@ type is required and the load function used.")
84 :new-p nil)) 93 :new-p nil))
85 "List of vectors defining how to determine what type of projects exist.") 94 "List of vectors defining how to determine what type of projects exist.")
86 95
96(put 'ede-project-class-files 'risky-local-variable t)
97
87;;; EDE project-autoload methods 98;;; EDE project-autoload methods
88;; 99;;
89(defmethod ede-project-root ((this ede-project-autoload)) 100(defmethod ede-project-root ((this ede-project-autoload))
@@ -122,6 +133,19 @@ Return nil if the project file does not exist."
122 (when (and f (file-exists-p f)) 133 (when (and f (file-exists-p f))
123 f))) 134 f)))
124 135
136(defmethod ede-auto-load-project ((this ede-project-autoload) dir)
137 "Load in the project associated with THIS project autoload description.
138THIS project description should be valid for DIR, where the project will
139be loaded."
140 ;; Last line of defense: don't load unsafe projects.
141 (when (not (or (oref this :safe-p)
142 (ede-directory-safe-p dir)))
143 (error "Attempt to load an unsafe project (bug elsewhere in EDE)"))
144 ;; Things are good - so load the project.
145 (let ((o (funcall (oref this load-type) dir)))
146 (when (not o)
147 (error "Project type error: :load-type failed to create a project"))
148 (ede-add-project-to-global-list o)))
125 149
126(provide 'ede/auto) 150(provide 'ede/auto)
127 151
diff --git a/lisp/cedet/ede/simple.el b/lisp/cedet/ede/simple.el
index 14b3a043a6d..829c085eade 100644
--- a/lisp/cedet/ede/simple.el
+++ b/lisp/cedet/ede/simple.el
@@ -50,7 +50,8 @@
50 :name "Simple" :file 'ede/simple 50 :name "Simple" :file 'ede/simple
51 :proj-file 'ede-simple-projectfile-for-dir 51 :proj-file 'ede-simple-projectfile-for-dir
52 :load-type 'ede-simple-load 52 :load-type 'ede-simple-load
53 :class-sym 'ede-simple-project) 53 :class-sym 'ede-simple-project
54 :safe-p nil)
54 t) 55 t)
55 56
56(defcustom ede-simple-save-directory "~/.ede" 57(defcustom ede-simple-save-directory "~/.ede"